QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
Vehicle.cc
Go to the documentation of this file.
1#include "Vehicle.h"
2#include "Actuators.h"
3#include "VehicleSupports.h"
5#include "AudioOutput.h"
6#include "AutoPilotPlugin.h"
8#include "EventHandler.h"
9#include "FirmwarePlugin.h"
11#include "FTPManager.h"
12#include "GeoFenceManager.h"
15#include "Joystick.h"
16#include "JoystickManager.h"
17#include "LinkManager.h"
18#include "MAVLinkLogManager.h"
19#include "MAVLinkProtocol.h"
20#include "MissionCommandTree.h"
21#include "MissionManager.h"
22#include "MultiVehicleManager.h"
23#include "ParameterManager.h"
25#include "PositionManager.h"
26#include "QGC.h"
27#include "QGCApplication.h"
28#include "QGCCameraManager.h"
29#include "QGCCorePlugin.h"
30#include "QGCImageProvider.h"
31#include "QGCLoggingCategory.h"
32#include "QGCQGeoCoordinate.h"
33#include "RallyPointManager.h"
34#include "RemoteIDManager.h"
35#include "SettingsManager.h"
36#include "AppSettings.h"
37#include "FlyViewSettings.h"
38#include "StandardModes.h"
40#include "TerrainQuery.h"
41#include "TrajectoryPoints.h"
42#include "VehicleLinkManager.h"
44#include "VideoManager.h"
45#include "VideoSettings.h"
46#include "DeviceInfo.h"
47#include "StatusTextHandler.h"
48#include "MAVLinkSigning.h"
49#include "GimbalController.h"
50#include "MavlinkSettings.h"
51#include "APM.h"
52
53#ifdef QT_DEBUG
54#include "MockLink.h"
55#endif
56
57#include <QtCore/QDateTime>
58
59QGC_LOGGING_CATEGORY(VehicleLog, "Vehicle.Vehicle")
60
61#define UPDATE_TIMER 50
62#define DEFAULT_LAT 38.965767f
63#define DEFAULT_LON -120.083923f
64#define SET_HOME_TERRAIN_ALT_MAX 10000
65#define SET_HOME_TERRAIN_ALT_MIN -500
66
67// After a second GCS has requested control and we have given it permission to takeover, we will remove takeover permission automatically after this timeout
68// If the second GCS didn't get control
69#define REQUEST_OPERATOR_CONTROL_ALLOW_TAKEOVER_TIMEOUT_MSECS 10000
70
71const QString guided_mode_not_supported_by_vehicle = QObject::tr("Guided mode not supported by Vehicle.");
72
73// Standard connected vehicle
75 int vehicleId,
76 int defaultComponentId,
77 MAV_AUTOPILOT firmwareType,
78 MAV_TYPE vehicleType,
79 QObject* parent)
80 : VehicleFactGroup (parent)
81 , _systemID (vehicleId)
82 , _defaultComponentId (defaultComponentId)
83 , _firmwareType (firmwareType)
84 , _vehicleType (vehicleType)
85 , _defaultCruiseSpeed (SettingsManager::instance()->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble())
86 , _defaultHoverSpeed (SettingsManager::instance()->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble())
87 , _trajectoryPoints (new TrajectoryPoints(this, this))
88 , _mavlinkStreamConfig (std::bind(&Vehicle::_setMessageInterval, this, std::placeholders::_1, std::placeholders::_2))
89 , _vehicleFactGroup (this)
90 , _gpsFactGroup (this)
91 , _gps2FactGroup (this)
92 , _gpsAggregateFactGroup (this)
93 , _windFactGroup (this)
94 , _vibrationFactGroup (this)
95 , _temperatureFactGroup (this)
96 , _clockFactGroup (this)
97 , _setpointFactGroup (this)
98 , _distanceSensorFactGroup (this)
99 , _localPositionFactGroup (this)
100 , _localPositionSetpointFactGroup(this)
101 , _estimatorStatusFactGroup (this)
102 , _hygrometerFactGroup (this)
103 , _generatorFactGroup (this)
104 , _efiFactGroup (this)
105 , _rpmFactGroup (this)
106 , _terrainFactGroup (this)
107 , _terrainProtocolHandler (new TerrainProtocolHandler(this, &_terrainFactGroup, this))
108{
109 connect(MultiVehicleManager::instance(), &MultiVehicleManager::activeVehicleChanged, this, &Vehicle::_activeVehicleChanged);
110
111 connect(MAVLinkProtocol::instance(), &MAVLinkProtocol::messageReceived, this, &Vehicle::_mavlinkMessageReceived);
112 connect(MAVLinkProtocol::instance(), &MAVLinkProtocol::mavlinkMessageStatus, this, &Vehicle::_mavlinkMessageStatus);
113
114 connect(this, &Vehicle::flightModeChanged, this, &Vehicle::_handleFlightModeChanged);
115 connect(this, &Vehicle::armedChanged, this, &Vehicle::_announceArmedChanged);
116 connect(this, &Vehicle::flyingChanged, this, [this](bool flying){
117 if (flying) {
120 }
121 });
122
123 connect(MultiVehicleManager::instance(), &MultiVehicleManager::parameterReadyVehicleAvailableChanged, this, &Vehicle::_vehicleParamLoaded);
124
125 connect(this, &Vehicle::remoteControlRSSIChanged, this, &Vehicle::_remoteControlRSSIChanged);
126
127 _commonInit(link);
128
129 // Set video stream to udp if running ArduSub and Video is disabled
130 if (sub() && SettingsManager::instance()->videoSettings()->videoSource()->rawValue() == VideoSettings::videoDisabled) {
131 SettingsManager::instance()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceUDPH264);
132 SettingsManager::instance()->videoSettings()->lowLatencyMode()->setRawValue(true);
133 }
134
135 _autopilotPlugin = _firmwarePlugin->autopilotPlugin(this);
136 _autopilotPlugin->setParent(this);
137
138 // PreArm Error self-destruct timer
139 connect(&_prearmErrorTimer, &QTimer::timeout, this, &Vehicle::_prearmErrorTimeout);
140 _prearmErrorTimer.setInterval(_prearmErrorTimeoutMSecs);
141 _prearmErrorTimer.setSingleShot(true);
142
143 // Send MAV_CMD ack timer
144 _mavCommandResponseCheckTimer.setSingleShot(false);
145 _mavCommandResponseCheckTimer.setInterval(_mavCommandResponseCheckTimeoutMSecs());
146 _mavCommandResponseCheckTimer.start();
147 connect(&_mavCommandResponseCheckTimer, &QTimer::timeout, this, &Vehicle::_sendMavCommandResponseTimeoutCheck);
148
149 // MAV_TYPE_GENERIC is used by unit test for creating a vehicle which doesn't do the connect sequence. This
150 // way we can test the methods that are used within the connect sequence.
151 if (!qgcApp()->runningUnitTests() || _vehicleType != MAV_TYPE_GENERIC) {
153 }
154
155 _firmwarePlugin->initializeVehicle(this);
156 for(auto& factName: factNames()) {
157 _firmwarePlugin->adjustMetaData(vehicleType, getFact(factName)->metaData());
158 }
159
160 _sendMultipleTimer.start(_sendMessageMultipleIntraMessageDelay);
161 connect(&_sendMultipleTimer, &QTimer::timeout, this, &Vehicle::_sendMessageMultipleNext);
162
163 connect(&_orbitTelemetryTimer, &QTimer::timeout, this, &Vehicle::_orbitTelemetryTimeout);
164
165 // Start csv logger
166 connect(&_csvLogTimer, &QTimer::timeout, this, &Vehicle::_writeCsvLine);
167 _csvLogTimer.start(1000);
168
169 // Start timer to limit altitude above terrain queries
171}
172
173// Disconnected Vehicle for offline editing
174Vehicle::Vehicle(MAV_AUTOPILOT firmwareType,
175 MAV_TYPE vehicleType,
176 QObject* parent)
177 : VehicleFactGroup (parent)
178 , _systemID (0)
179 , _defaultComponentId (MAV_COMP_ID_ALL)
180 , _offlineEditingVehicle (true)
181 , _firmwareType (firmwareType)
182 , _vehicleType (vehicleType)
183 , _defaultCruiseSpeed (SettingsManager::instance()->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble())
184 , _defaultHoverSpeed (SettingsManager::instance()->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble())
185 , _capabilityBitsKnown (true)
186 , _capabilityBits (MAV_PROTOCOL_CAPABILITY_MISSION_FENCE | MAV_PROTOCOL_CAPABILITY_MISSION_RALLY)
187 , _trajectoryPoints (new TrajectoryPoints(this, this))
188 , _mavlinkStreamConfig (std::bind(&Vehicle::_setMessageInterval, this, std::placeholders::_1, std::placeholders::_2))
189 , _vehicleFactGroup (this)
190 , _gpsFactGroup (this)
191 , _gps2FactGroup (this)
192 , _gpsAggregateFactGroup (this)
193 , _windFactGroup (this)
194 , _vibrationFactGroup (this)
195 , _clockFactGroup (this)
196 , _distanceSensorFactGroup (this)
197 , _localPositionFactGroup (this)
198 , _localPositionSetpointFactGroup (this)
199{
200 // This will also set the settings based firmware/vehicle types. So it needs to happen first.
201 if (_firmwareType == MAV_AUTOPILOT_TRACK) {
203 }
204
205 _commonInit(nullptr /* link */);
206
207 connect(SettingsManager::instance()->appSettings()->offlineEditingCruiseSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged);
208 connect(SettingsManager::instance()->appSettings()->offlineEditingHoverSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged);
209
210 _offlineFirmwareTypeSettingChanged(_firmwareType); // This adds correct terrain capability bit
211 _firmwarePlugin->initializeVehicle(this);
212}
213
215{
216 connect(SettingsManager::instance()->appSettings()->offlineEditingFirmwareClass(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged);
217 connect(SettingsManager::instance()->appSettings()->offlineEditingVehicleClass(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged);
218
219 _offlineFirmwareTypeSettingChanged(SettingsManager::instance()->appSettings()->offlineEditingFirmwareClass()->rawValue());
220 _offlineVehicleTypeSettingChanged(SettingsManager::instance()->appSettings()->offlineEditingVehicleClass()->rawValue());
221}
222
224{
225 disconnect(SettingsManager::instance()->appSettings()->offlineEditingFirmwareClass(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged);
226 disconnect(SettingsManager::instance()->appSettings()->offlineEditingVehicleClass(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged);
227}
228
229void Vehicle::_commonInit(LinkInterface* link)
230{
231 _firmwarePlugin = FirmwarePluginManager::instance()->firmwarePluginForAutopilot(_firmwareType, _vehicleType);
232
234
235 connect(this, &Vehicle::coordinateChanged, this, &Vehicle::_updateDistanceHeadingHome);
236 connect(this, &Vehicle::coordinateChanged, this, &Vehicle::_updateDistanceHeadingGCS);
237 connect(this, &Vehicle::homePositionChanged, this, &Vehicle::_updateDistanceHeadingHome);
238 connect(this, &Vehicle::hobbsMeterChanged, this, &Vehicle::_updateHobbsMeter);
239 connect(this, &Vehicle::coordinateChanged, this, &Vehicle::_updateAltAboveTerrain);
242
243 connect(QGCPositionManager::instance(), &QGCPositionManager::gcsPositionChanged, this, &Vehicle::_updateDistanceHeadingGCS);
244 connect(QGCPositionManager::instance(), &QGCPositionManager::gcsPositionChanged, this, &Vehicle::_updateHomepoint);
245
247 connect(_missionManager, &MissionManager::error, this, &Vehicle::_missionManagerError);
248 connect(_missionManager, &MissionManager::newMissionItemsAvailable, this, &Vehicle::_firstMissionLoadComplete);
249 connect(_missionManager, &MissionManager::newMissionItemsAvailable, this, &Vehicle::_clearCameraTriggerPoints);
250 connect(_missionManager, &MissionManager::sendComplete, this, &Vehicle::_clearCameraTriggerPoints);
251 connect(_missionManager, &MissionManager::currentIndexChanged, this, &Vehicle::_updateHeadingToNextWP);
252 connect(_missionManager, &MissionManager::currentIndexChanged, this, &Vehicle::_updateMissionItemIndex);
253
256
257 _standardModes = new StandardModes (this, this);
258 _componentInformationManager = new ComponentInformationManager (this, this);
260 _ftpManager = new FTPManager (this);
261
263 if (link) {
264 _vehicleLinkManager->_addLink(link);
265 }
266
268 // Re-emit flightModeChanged after available modes mapping updates so UI refreshes
269 // the human-readable mode name even if HEARTBEAT arrived earlier.
270 connect(_standardModes, &StandardModes::modesUpdated, this, [this]() {
272 });
273
274 _parameterManager = new ParameterManager(this);
275 connect(_parameterManager, &ParameterManager::parametersReadyChanged, this, &Vehicle::_parametersReady);
276 connect(_parameterManager, &ParameterManager::parametersReadyChanged, this, [this](bool) {
277 emit hasGripperChanged();
278 });
280 this, &Vehicle::_gotProgressUpdate);
281 connect(_parameterManager, &ParameterManager::loadProgressChanged, this, &Vehicle::_gotProgressUpdate);
282
283 _objectAvoidance = new VehicleObjectAvoidance(this, this);
284
285 _autotune = _firmwarePlugin->createAutotune(this);
286
287 // GeoFenceManager needs to access ParameterManager so make sure to create after
289 connect(_geoFenceManager, &GeoFenceManager::error, this, &Vehicle::_geoFenceManagerError);
290 connect(_geoFenceManager, &GeoFenceManager::loadComplete, this, &Vehicle::_firstGeoFenceLoadComplete);
291
293 connect(_rallyPointManager, &RallyPointManager::error, this, &Vehicle::_rallyPointManagerError);
294 connect(_rallyPointManager, &RallyPointManager::loadComplete, this, &Vehicle::_firstRallyPointLoadComplete);
295
296 // Remote ID manager might want to acces parameters so make sure to create it after
298
299 // Flight modes can differ based on advanced mode
300 connect(QGCCorePlugin::instance(), &QGCCorePlugin::showAdvancedUIChanged, this, &Vehicle::flightModesChanged);
301
303
304 _createImageProtocolManager();
305 _createStatusTextHandler();
306 _createMAVLinkLogManager();
307
308 // _addFactGroup(_vehicleFactGroup, _vehicleFactGroupName);
326
327 // Add firmware-specific fact groups, if provided
328 QMap<QString, FactGroup*>* fwFactGroups = _firmwarePlugin->factGroups();
329 if (fwFactGroups) {
330 for (auto it = fwFactGroups->keyValueBegin(); it != fwFactGroups->keyValueEnd(); ++it) {
331 _addFactGroup(it->second, it->first);
332 }
333 }
334
335 _flightTimeUpdater.setInterval(1000);
336 _flightTimeUpdater.setSingleShot(false);
337 connect(&_flightTimeUpdater, &QTimer::timeout, this, &Vehicle::_updateFlightTime);
338
339 // Set video stream to udp if running ArduSub and Video is disabled
340 if (sub() && SettingsManager::instance()->videoSettings()->videoSource()->rawValue() == VideoSettings::videoDisabled) {
341 SettingsManager::instance()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceUDPH264);
342 SettingsManager::instance()->videoSettings()->lowLatencyMode()->setRawValue(true);
343 }
344
345 _gimbalController = new GimbalController(this);
346 _vehicleSupports = new VehicleSupports(this);
347
348 _createCameraManager();
349}
350
352{
353 qCDebug(VehicleLog) << "~Vehicle" << this;
354
355 // Stop all timers and disconnect their signals to prevent any callbacks during destruction.
356 // Even though _stopCommandProcessing() should have been called earlier via VehicleLinkManager,
357 // we do it again here defensively in case the vehicle is destroyed without going through
358 // the normal link removal path (e.g., in unit tests).
359 _mavCommandResponseCheckTimer.stop();
360 _mavCommandResponseCheckTimer.disconnect();
361 _sendMultipleTimer.stop();
362 _sendMultipleTimer.disconnect();
363 _prearmErrorTimer.stop();
364 _prearmErrorTimer.disconnect();
365
366 delete _missionManager;
367 _missionManager = nullptr;
368
369 delete _autopilotPlugin;
370 _autopilotPlugin = nullptr;
371}
372
373void Vehicle::_deleteCameraManager()
374{
375 if(_cameraManager) {
376 // Disconnect all signals to prevent any callbacks during or after deletion
377 _cameraManager->disconnect();
378 delete _cameraManager;
379 _cameraManager = nullptr;
380 }
381}
382
383void Vehicle::_deleteGimbalController()
384{
385 if (_gimbalController) {
386 // Disconnect all signals to prevent any callbacks during or after deletion
387 _gimbalController->disconnect();
388 delete _gimbalController;
389 _gimbalController = nullptr;
390 }
391}
392
393void Vehicle::_stopCommandProcessing()
394{
395 qCDebug(VehicleLog) << "_stopCommandProcessing - stopping timers and clearing pending commands";
396
397 // Stop timers AND disconnect their signals to prevent any pending callbacks
398 // from being delivered after this point. This is critical during vehicle destruction
399 // where a queued callback could access a partially-destroyed vehicle.
400 _mavCommandResponseCheckTimer.stop();
401 _mavCommandResponseCheckTimer.disconnect();
402 _sendMultipleTimer.stop();
403 _sendMultipleTimer.disconnect();
404
405 // Clear pending commands without calling callbacks (vehicle is being destroyed)
406 _mavCommandList.clear();
407
408 // Clear request message info map - delete the allocated RequestMessageInfo objects
409 for (auto& compIdEntry : _requestMessageInfoMap) {
410 qDeleteAll(compIdEntry);
411 }
412 _requestMessageInfoMap.clear();
413
414 for (auto& requestQueue : _requestMessageQueueMap) {
415 qDeleteAll(requestQueue);
416 }
417 _requestMessageQueueMap.clear();
418}
419
421{
422 _firmwareType = static_cast<MAV_AUTOPILOT>(varFirmwareType.toInt());
423 _firmwarePlugin = FirmwarePluginManager::instance()->firmwarePluginForAutopilot(_firmwareType, _vehicleType);
424 if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) {
425 _capabilityBits |= MAV_PROTOCOL_CAPABILITY_TERRAIN;
426 } else {
427 _capabilityBits &= ~MAV_PROTOCOL_CAPABILITY_TERRAIN;
428 }
429 emit firmwareTypeChanged();
430 emit capabilityBitsChanged(_capabilityBits);
431}
432
434{
435 _vehicleType = static_cast<MAV_TYPE>(varVehicleType.toInt());
436 emit vehicleTypeChanged();
437}
438
439void Vehicle::_offlineCruiseSpeedSettingChanged(QVariant value)
440{
441 _defaultCruiseSpeed = value.toDouble();
442 emit defaultCruiseSpeedChanged(_defaultCruiseSpeed);
443}
444
445void Vehicle::_offlineHoverSpeedSettingChanged(QVariant value)
446{
447 _defaultHoverSpeed = value.toDouble();
448 emit defaultHoverSpeedChanged(_defaultHoverSpeed);
449}
450
452{
453 return QGCMAVLink::firmwareClassToString(_firmwareType);
454}
455
456void Vehicle::resetCounters()
457{
458 _messagesReceived = 0;
459 _messagesSent = 0;
460 _messagesLost = 0;
461 _messageSeq = 0;
462 _heardFrom = false;
463}
464
465void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message)
466{
467 if (message.sysid != _systemID && message.sysid != 0) {
468 // We allow RADIO_STATUS messages which come from a link the vehicle is using to pass through and be handled
469 if (!(message.msgid == MAVLINK_MSG_ID_RADIO_STATUS && _vehicleLinkManager->containsLink(link))) {
470 return;
471 }
472 }
473
474 // We give the link manager first whack since it it reponsible for adding new links
476
477 //-- Check link status
478 _messagesReceived++;
480 if(!_heardFrom) {
481 if(message.msgid == MAVLINK_MSG_ID_HEARTBEAT) {
482 _heardFrom = true;
483 _compID = message.compid;
484 _messageSeq = message.seq + 1;
485 }
486 } else {
487 if(_compID == message.compid) {
488 uint16_t seq_received = static_cast<uint16_t>(message.seq);
489 uint16_t packet_lost_count = 0;
490 //-- Account for overflow during packet loss
491 if(seq_received < _messageSeq) {
492 packet_lost_count = (seq_received + 255) - _messageSeq;
493 } else {
494 packet_lost_count = seq_received - _messageSeq;
495 }
496 _messageSeq = message.seq + 1;
497 _messagesLost += packet_lost_count;
498 if(packet_lost_count)
499 emit messagesLostChanged();
500 }
501 }
502
503 // Give the plugin a change to adjust the message contents
504 if (!_firmwarePlugin->adjustIncomingMavlinkMessage(this, &message)) {
505 return;
506 }
507
508 // Give the Core Plugin access to all mavlink traffic
509 if (!QGCCorePlugin::instance()->mavlinkMessage(this, link, message)) {
510 return;
511 }
512
514 return;
515 }
516 _ftpManager->_mavlinkMessageReceived(message);
517 _parameterManager->mavlinkMessageReceived(message);
518 (void) QMetaObject::invokeMethod(_imageProtocolManager, "mavlinkMessageReceived", Qt::AutoConnection, message);
520
521 _waitForMavlinkMessageMessageReceivedHandler(message);
522
523 // Handle creation of dynamic fact group lists
526
527 // Let the fact groups take a whack at the mavlink traffic
528 for (FactGroup* factGroup : factGroups()) {
529 factGroup->handleMessage(this, message);
530 }
531
532 this->handleMessage(this, message);
533
534 switch (message.msgid) {
535 case MAVLINK_MSG_ID_HOME_POSITION:
536 _handleHomePosition(message);
537 break;
538 case MAVLINK_MSG_ID_HEARTBEAT:
539 _handleHeartbeat(message);
540 break;
541 case MAVLINK_MSG_ID_RADIO_STATUS:
542 _handleRadioStatus(message);
543 break;
544 case MAVLINK_MSG_ID_RC_CHANNELS:
545 _handleRCChannels(message);
546 break;
547 case MAVLINK_MSG_ID_BATTERY_STATUS:
548 _handleBatteryStatus(message);
549 break;
550 case MAVLINK_MSG_ID_SYS_STATUS:
551 _handleSysStatus(message);
552 break;
553 case MAVLINK_MSG_ID_EXTENDED_SYS_STATE:
554 _handleExtendedSysState(message);
555 break;
556 case MAVLINK_MSG_ID_COMMAND_ACK:
557 _handleCommandAck(message);
558 break;
559 case MAVLINK_MSG_ID_LOGGING_DATA:
560 _handleMavlinkLoggingData(message);
561 break;
562 case MAVLINK_MSG_ID_LOGGING_DATA_ACKED:
563 _handleMavlinkLoggingDataAcked(message);
564 break;
565 case MAVLINK_MSG_ID_GPS_RAW_INT:
566 _handleGpsRawInt(message);
567 break;
568 case MAVLINK_MSG_ID_GLOBAL_POSITION_INT:
569 _handleGlobalPositionInt(message);
570 break;
571 case MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED:
572 _handleCameraImageCaptured(message);
573 break;
574 case MAVLINK_MSG_ID_ADSB_VEHICLE:
576 break;
577 case MAVLINK_MSG_ID_HIGH_LATENCY:
578 _handleHighLatency(message);
579 break;
580 case MAVLINK_MSG_ID_HIGH_LATENCY2:
581 _handleHighLatency2(message);
582 break;
583 case MAVLINK_MSG_ID_STATUSTEXT:
584 m_statusTextHandler->mavlinkMessageReceived(message);
585 break;
586 case MAVLINK_MSG_ID_ORBIT_EXECUTION_STATUS:
587 _handleOrbitExecutionStatus(message);
588 break;
589 case MAVLINK_MSG_ID_PING:
590 _handlePing(link, message);
591 break;
592 case MAVLINK_MSG_ID_OBSTACLE_DISTANCE:
593 _handleObstacleDistance(message);
594 break;
595 case MAVLINK_MSG_ID_FENCE_STATUS:
596 _handleFenceStatus(message);
597 break;
598
599 case MAVLINK_MSG_ID_EVENT:
600 case MAVLINK_MSG_ID_CURRENT_EVENT_SEQUENCE:
601 case MAVLINK_MSG_ID_RESPONSE_EVENT_ERROR:
602 _eventHandler(message.compid).handleEvents(message);
603 break;
604
605 case MAVLINK_MSG_ID_SERIAL_CONTROL:
606 {
607 mavlink_serial_control_t ser;
608 mavlink_msg_serial_control_decode(&message, &ser);
609 if (static_cast<size_t>(ser.count) > sizeof(ser.data)) {
610 qCWarning(VehicleLog) << "Invalid count for SERIAL_CONTROL, discarding." << ser.count;
611 } else {
612 emit mavlinkSerialControl(ser.device, ser.flags, ser.timeout, ser.baudrate,
613 QByteArray(reinterpret_cast<const char*>(ser.data), ser.count));
614 }
615 }
616 break;
617 case MAVLINK_MSG_ID_AVAILABLE_MODES_MONITOR:
618 {
619 // Avoid duplicate requests during initial connection setup
621 mavlink_available_modes_monitor_t availableModesMonitor;
622 mavlink_msg_available_modes_monitor_decode(&message, &availableModesMonitor);
623 _standardModes->availableModesMonitorReceived(availableModesMonitor.seq);
624 }
625 break;
626 }
627 case MAVLINK_MSG_ID_CURRENT_MODE:
628 _handleCurrentMode(message);
629 break;
630
631 // Following are ArduPilot dialect messages
632#if !defined(QGC_NO_ARDUPILOT_DIALECT)
633 case MAVLINK_MSG_ID_CAMERA_FEEDBACK:
634 _handleCameraFeedback(message);
635 break;
636#endif
637 case MAVLINK_MSG_ID_LOG_ENTRY:
638 {
639 mavlink_log_entry_t log{};
640 mavlink_msg_log_entry_decode(&message, &log);
641 emit logEntry(log.time_utc, log.size, log.id, log.num_logs, log.last_log_num);
642 break;
643 }
644 case MAVLINK_MSG_ID_LOG_DATA:
645 {
646 mavlink_log_data_t log{};
647 mavlink_msg_log_data_decode(&message, &log);
648 if (static_cast<size_t>(log.count) > sizeof(log.data)) {
649 qCWarning(VehicleLog) << "Invalid count for LOG_DATA, discarding." << log.count;
650 } else {
651 emit logData(log.ofs, log.id, log.count, log.data);
652 }
653 break;
654 }
655 case MAVLINK_MSG_ID_MESSAGE_INTERVAL:
656 {
657 _handleMessageInterval(message);
658 break;
659 }
660 case MAVLINK_MSG_ID_CONTROL_STATUS:
661 _handleControlStatus(message);
662 break;
663 case MAVLINK_MSG_ID_COMMAND_LONG:
664 _handleCommandLong(message);
665 break;
666 }
667
668 // This must be emitted after the vehicle processes the message. This way the vehicle state is up to date when anyone else
669 // does processing.
670 emit mavlinkMessageReceived(message);
671}
672
673#if !defined(QGC_NO_ARDUPILOT_DIALECT)
674void Vehicle::_handleCameraFeedback(const mavlink_message_t& message)
675{
676 mavlink_camera_feedback_t feedback;
677
678 mavlink_msg_camera_feedback_decode(&message, &feedback);
679
680 QGeoCoordinate imageCoordinate((double)feedback.lat / qPow(10.0, 7.0), (double)feedback.lng / qPow(10.0, 7.0), feedback.alt_msl);
681 qCDebug(VehicleLog) << "_handleCameraFeedback coord:index" << imageCoordinate << feedback.img_idx;
682 _cameraTriggerPoints.append(new QGCQGeoCoordinate(imageCoordinate, this));
683}
684#endif
685
686void Vehicle::_handleOrbitExecutionStatus(const mavlink_message_t& message)
687{
688 mavlink_orbit_execution_status_t orbitStatus;
689
690 mavlink_msg_orbit_execution_status_decode(&message, &orbitStatus);
691
692 double newRadius = qAbs(static_cast<double>(orbitStatus.radius));
693 if (!QGC::fuzzyCompare(_orbitMapCircle.radius()->rawValue().toDouble(), newRadius)) {
694 _orbitMapCircle.radius()->setRawValue(newRadius);
695 }
696
697 bool newOrbitClockwise = orbitStatus.radius > 0 ? true : false;
698 if (_orbitMapCircle.clockwiseRotation() != newOrbitClockwise) {
699 _orbitMapCircle.setClockwiseRotation(newOrbitClockwise);
700 }
701
702 QGeoCoordinate newCenter(static_cast<double>(orbitStatus.x) / qPow(10.0, 7.0), static_cast<double>(orbitStatus.y) / qPow(10.0, 7.0));
703 if (_orbitMapCircle.center() != newCenter) {
704 _orbitMapCircle.setCenter(newCenter);
705 }
706
707 if (!_orbitActive) {
708 _orbitActive = true;
709 _orbitMapCircle.setShowRotation(true);
710 emit orbitActiveChanged(true);
711 }
712
713 _orbitTelemetryTimer.start(_orbitTelemetryTimeoutMsecs);
714}
715
716void Vehicle::_orbitTelemetryTimeout()
717{
718 _orbitActive = false;
719 emit orbitActiveChanged(false);
720}
721
722void Vehicle::_handleCameraImageCaptured(const mavlink_message_t& message)
723{
724 mavlink_camera_image_captured_t feedback;
725
726 mavlink_msg_camera_image_captured_decode(&message, &feedback);
727
728 QGeoCoordinate imageCoordinate((double)feedback.lat / qPow(10.0, 7.0), (double)feedback.lon / qPow(10.0, 7.0), feedback.alt);
729 qCDebug(VehicleLog) << "_handleCameraFeedback coord:index" << imageCoordinate << feedback.image_index << feedback.capture_result;
730 if (feedback.capture_result == 1) {
731 _cameraTriggerPoints.append(new QGCQGeoCoordinate(imageCoordinate, this));
732 }
733}
734
735// TODO: VehicleFactGroup
736void Vehicle::_handleGpsRawInt(mavlink_message_t& message)
737{
738 mavlink_gps_raw_int_t gpsRawInt;
739 mavlink_msg_gps_raw_int_decode(&message, &gpsRawInt);
740
741 _gpsRawIntMessageAvailable = true;
742
743 if (gpsRawInt.fix_type >= GPS_FIX_TYPE_3D_FIX) {
744 if (!_globalPositionIntMessageAvailable) {
745 QGeoCoordinate newPosition(gpsRawInt.lat / (double)1E7, gpsRawInt.lon / (double)1E7, gpsRawInt.alt / 1000.0);
746 if (newPosition != _coordinate) {
747 _coordinate = newPosition;
748 emit coordinateChanged(_coordinate);
749 }
751 _altitudeAMSLFact.setRawValue(gpsRawInt.alt / 1000.0);
752 }
753 }
754 }
755}
756
757// TODO: VehicleFactGroup
758void Vehicle::_handleGlobalPositionInt(mavlink_message_t& message)
759{
760 mavlink_global_position_int_t globalPositionInt;
761 mavlink_msg_global_position_int_decode(&message, &globalPositionInt);
762
764 _altitudeRelativeFact.setRawValue(globalPositionInt.relative_alt / 1000.0);
765 _altitudeAMSLFact.setRawValue(globalPositionInt.alt / 1000.0);
766 }
767
768 // ArduPilot sends bogus GLOBAL_POSITION_INT messages with lat/lat 0/0 even when it has no gps signal
769 // Apparently, this is in order to transport relative altitude information.
770 if (globalPositionInt.lat == 0 && globalPositionInt.lon == 0) {
771 return;
772 }
773
774 _globalPositionIntMessageAvailable = true;
775 QGeoCoordinate newPosition(globalPositionInt.lat / (double)1E7, globalPositionInt.lon / (double)1E7, globalPositionInt.alt / 1000.0);
776 if (newPosition != _coordinate) {
777 _coordinate = newPosition;
778 emit coordinateChanged(_coordinate);
779 }
780}
781
782// TODO: VehicleFactGroup
783void Vehicle::_handleHighLatency(mavlink_message_t& message)
784{
785 mavlink_high_latency_t highLatency;
786 mavlink_msg_high_latency_decode(&message, &highLatency);
787
788 QString previousFlightMode;
789 if (_base_mode != 0 || _custom_mode != 0){
790 // Vehicle is initialized with _base_mode=0 and _custom_mode=0. Don't pass this to flightMode() since it will complain about
791 // bad modes while unit testing.
792 previousFlightMode = flightMode();
793 }
794 _base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED;
795 _custom_mode = _firmwarePlugin->highLatencyCustomModeTo32Bits(highLatency.custom_mode);
796 if (previousFlightMode != flightMode()) {
798 }
799
800 // Assume armed since we don't know
801 if (_armed != true) {
802 _armed = true;
803 emit armedChanged(_armed);
804 }
805
806 struct {
807 const double latitude;
808 const double longitude;
809 const double altitude;
810 } coordinate {
811 highLatency.latitude / (double)1E7,
812 highLatency.longitude / (double)1E7,
813 static_cast<double>(highLatency.altitude_amsl)
814 };
815
816 _coordinate.setLatitude(coordinate.latitude);
817 _coordinate.setLongitude(coordinate.longitude);
818 _coordinate.setAltitude(coordinate.altitude);
819 emit coordinateChanged(_coordinate);
820
821 _airSpeedFact.setRawValue((double)highLatency.airspeed / 5.0);
822 _groundSpeedFact.setRawValue((double)highLatency.groundspeed / 5.0);
823 _climbRateFact.setRawValue((double)highLatency.climb_rate / 10.0);
824 _headingFact.setRawValue((double)highLatency.heading * 2.0);
825 _altitudeRelativeFact.setRawValue(qQNaN());
826 _altitudeAMSLFact.setRawValue(coordinate.altitude);
827}
828
829// TODO: VehicleFactGroup
830void Vehicle::_handleHighLatency2(mavlink_message_t& message)
831{
832 mavlink_high_latency2_t highLatency2;
833 mavlink_msg_high_latency2_decode(&message, &highLatency2);
834
835 QString previousFlightMode;
836 if (_base_mode != 0 || _custom_mode != 0){
837 // Vehicle is initialized with _base_mode=0 and _custom_mode=0. Don't pass this to flightMode() since it will complain about
838 // bad modes while unit testing.
839 previousFlightMode = flightMode();
840 }
841 // ArduPilot has the basemode in the custom0 field of the high latency message.
842 if (highLatency2.autopilot == MAV_AUTOPILOT_ARDUPILOTMEGA) {
843 _base_mode = (uint8_t)highLatency2.custom0;
844 } else {
845 _base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED;
846 }
847 _custom_mode = _firmwarePlugin->highLatencyCustomModeTo32Bits(highLatency2.custom_mode);
848 if (previousFlightMode != flightMode()) {
850 }
851 // ArduPilot has the arming status (basemode) in the custom0 field of the high latency message.
852 if (highLatency2.autopilot == MAV_AUTOPILOT_ARDUPILOTMEGA) {
853 if ((uint8_t)highLatency2.custom0 & MAV_MODE_FLAG_SAFETY_ARMED && _armed != true) {
854 _armed = true;
855 emit armedChanged(_armed);
856 } else if (!((uint8_t)highLatency2.custom0 & MAV_MODE_FLAG_SAFETY_ARMED) && _armed != false) {
857 _armed = false;
858 emit armedChanged(_armed);
859 }
860 } else {
861 // Assume armed since we don't know
862 if (_armed != true) {
863 _armed = true;
864 emit armedChanged(_armed);
865 }
866 }
867
868 _coordinate.setLatitude(highLatency2.latitude / (double)1E7);
869 _coordinate.setLongitude(highLatency2.longitude / (double)1E7);
870 _coordinate.setAltitude(highLatency2.altitude);
871 emit coordinateChanged(_coordinate);
872
873 _airSpeedFact.setRawValue((double)highLatency2.airspeed / 5.0);
874 _groundSpeedFact.setRawValue((double)highLatency2.groundspeed / 5.0);
875 _climbRateFact.setRawValue((double)highLatency2.climb_rate / 10.0);
876 _headingFact.setRawValue((double)highLatency2.heading * 2.0);
877 _altitudeRelativeFact.setRawValue(qQNaN());
878 _altitudeAMSLFact.setRawValue(highLatency2.altitude);
879
880 // Map from MAV_FAILURE bits to standard SYS_STATUS message handling
881 const uint32_t newOnboardControlSensorsEnabled = QGCMAVLink::highLatencyFailuresToMavSysStatus(highLatency2);
882 if (newOnboardControlSensorsEnabled != _onboardControlSensorsEnabled) {
883 _onboardControlSensorsEnabled = newOnboardControlSensorsEnabled;
884 _onboardControlSensorsPresent = newOnboardControlSensorsEnabled;
885 _onboardControlSensorsUnhealthy = 0;
886 }
887}
888
889void Vehicle::_setCapabilities(uint64_t capabilityBits)
890{
891 _capabilityBits = capabilityBits;
892 _capabilityBitsKnown = true;
893 emit capabilitiesKnownChanged(true);
894 emit capabilityBitsChanged(_capabilityBits);
895
896 QString supports("supports");
897 QString doesNotSupport("does not support");
898
899 qCDebug(VehicleLog) << QString("Vehicle %1 Mavlink 2.0").arg(_capabilityBits & MAV_PROTOCOL_CAPABILITY_MAVLINK2 ? supports : doesNotSupport);
900 qCDebug(VehicleLog) << QString("Vehicle %1 MISSION_ITEM_INT").arg(_capabilityBits & MAV_PROTOCOL_CAPABILITY_MISSION_INT ? supports : doesNotSupport);
901 qCDebug(VehicleLog) << QString("Vehicle %1 MISSION_COMMAND_INT").arg(_capabilityBits & MAV_PROTOCOL_CAPABILITY_COMMAND_INT ? supports : doesNotSupport);
902 qCDebug(VehicleLog) << QString("Vehicle %1 GeoFence").arg(_capabilityBits & MAV_PROTOCOL_CAPABILITY_MISSION_FENCE ? supports : doesNotSupport);
903 qCDebug(VehicleLog) << QString("Vehicle %1 RallyPoints").arg(_capabilityBits & MAV_PROTOCOL_CAPABILITY_MISSION_RALLY ? supports : doesNotSupport);
904 qCDebug(VehicleLog) << QString("Vehicle %1 Terrain").arg(_capabilityBits & MAV_PROTOCOL_CAPABILITY_TERRAIN ? supports : doesNotSupport);
905}
906
908{
909 QString uid;
910 uint8_t* pUid = (uint8_t*)(void*)&_uid;
911 uid = uid.asprintf("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
912 pUid[0] & 0xff,
913 pUid[1] & 0xff,
914 pUid[2] & 0xff,
915 pUid[3] & 0xff,
916 pUid[4] & 0xff,
917 pUid[5] & 0xff,
918 pUid[6] & 0xff,
919 pUid[7] & 0xff);
920 return uid;
921}
922
923void Vehicle::_handleExtendedSysState(mavlink_message_t& message)
924{
925 mavlink_extended_sys_state_t extendedState;
926 mavlink_msg_extended_sys_state_decode(&message, &extendedState);
927
928 switch (extendedState.landed_state) {
929 case MAV_LANDED_STATE_ON_GROUND:
930 _setFlying(false);
931 _setLanding(false);
932 break;
933 case MAV_LANDED_STATE_TAKEOFF:
934 case MAV_LANDED_STATE_IN_AIR:
935 _setFlying(true);
936 _setLanding(false);
937 break;
938 case MAV_LANDED_STATE_LANDING:
939 _setFlying(true);
940 _setLanding(true);
941 break;
942 default:
943 break;
944 }
945
946 if (vtol()) {
947 bool vtolInFwdFlight = extendedState.vtol_state == MAV_VTOL_STATE_FW;
948 if (vtolInFwdFlight != _vtolInFwdFlight) {
949 _vtolInFwdFlight = vtolInFwdFlight;
951 }
952 }
953}
954
955bool Vehicle::_apmArmingNotRequired()
956{
957 QString armingRequireParam("ARMING_REQUIRE");
958 return _parameterManager->parameterExists(ParameterManager::defaultComponentId, armingRequireParam) &&
959 _parameterManager->getParameter(ParameterManager::defaultComponentId, armingRequireParam)->rawValue().toInt() == 0;
960}
961
962void Vehicle::_handleSysStatus(mavlink_message_t& message)
963{
964 if (message.compid != _defaultComponentId) {
965 return;
966 }
967
968 mavlink_sys_status_t sysStatus;
969 mavlink_msg_sys_status_decode(&message, &sysStatus);
970
971 _sysStatusSensorInfo.update(sysStatus);
972
973 if (sysStatus.onboard_control_sensors_enabled & MAV_SYS_STATUS_PREARM_CHECK) {
974 if (!_readyToFlyAvailable) {
975 _readyToFlyAvailable = true;
977 }
978
979 bool newReadyToFly = sysStatus.onboard_control_sensors_health & MAV_SYS_STATUS_PREARM_CHECK;
980 if (newReadyToFly != _readyToFly) {
981 _readyToFly = newReadyToFly;
982 emit readyToFlyChanged(_readyToFly);
983 }
984 }
985
986 bool newAllSensorsHealthy = (sysStatus.onboard_control_sensors_enabled & sysStatus.onboard_control_sensors_health) == sysStatus.onboard_control_sensors_enabled;
987 if (newAllSensorsHealthy != _allSensorsHealthy) {
988 _allSensorsHealthy = newAllSensorsHealthy;
989 emit allSensorsHealthyChanged(_allSensorsHealthy);
990 }
991
992 if (_onboardControlSensorsPresent != sysStatus.onboard_control_sensors_present) {
993 _onboardControlSensorsPresent = sysStatus.onboard_control_sensors_present;
994 emit sensorsPresentBitsChanged(_onboardControlSensorsPresent);
996 }
997 if (_onboardControlSensorsEnabled != sysStatus.onboard_control_sensors_enabled) {
998 _onboardControlSensorsEnabled = sysStatus.onboard_control_sensors_enabled;
999 emit sensorsEnabledBitsChanged(_onboardControlSensorsEnabled);
1000 }
1001 if (_onboardControlSensorsHealth != sysStatus.onboard_control_sensors_health) {
1002 _onboardControlSensorsHealth = sysStatus.onboard_control_sensors_health;
1003 emit sensorsHealthBitsChanged(_onboardControlSensorsHealth);
1004 }
1005
1006 // ArduPilot firmare has a strange case when ARMING_REQUIRE=0. This means the vehicle is always armed but the motors are not
1007 // really powered up until the safety button is pressed. Because of this we can't depend on the heartbeat to tell us the true
1008 // armed (and dangerous) state. We must instead rely on SYS_STATUS telling us that the motors are enabled.
1009 if (apmFirmware() && _apmArmingNotRequired()) {
1010 _updateArmed(_onboardControlSensorsEnabled & MAV_SYS_STATUS_SENSOR_MOTOR_OUTPUTS);
1011 }
1012
1013 uint32_t newSensorsUnhealthy = _onboardControlSensorsEnabled & ~_onboardControlSensorsHealth;
1014 if (newSensorsUnhealthy != _onboardControlSensorsUnhealthy) {
1015 _onboardControlSensorsUnhealthy = newSensorsUnhealthy;
1016 emit sensorsUnhealthyBitsChanged(_onboardControlSensorsUnhealthy);
1017 }
1018}
1019
1020void Vehicle::_handleBatteryStatus(mavlink_message_t& message)
1021{
1022 mavlink_battery_status_t batteryStatus;
1023 mavlink_msg_battery_status_decode(&message, &batteryStatus);
1024
1025 if (!_lowestBatteryChargeStateAnnouncedMap.contains(batteryStatus.id)) {
1026 _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id] = batteryStatus.charge_state;
1027 }
1028
1029 QString batteryMessage;
1030
1031 switch (batteryStatus.charge_state) {
1032 case MAV_BATTERY_CHARGE_STATE_OK:
1033 _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id] = batteryStatus.charge_state;
1034 break;
1035 case MAV_BATTERY_CHARGE_STATE_LOW:
1036 if (batteryStatus.charge_state > _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id]) {
1037 _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id] = batteryStatus.charge_state;
1038 batteryMessage = tr("battery %1 level low");
1039 }
1040 break;
1041 case MAV_BATTERY_CHARGE_STATE_CRITICAL:
1042 if (batteryStatus.charge_state > _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id]) {
1043 _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id] = batteryStatus.charge_state;
1044 batteryMessage = tr("battery %1 level is critical");
1045 }
1046 break;
1047 case MAV_BATTERY_CHARGE_STATE_EMERGENCY:
1048 if (batteryStatus.charge_state > _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id]) {
1049 _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id] = batteryStatus.charge_state;
1050 batteryMessage = tr("battery %1 level emergency");
1051 }
1052 break;
1053 case MAV_BATTERY_CHARGE_STATE_FAILED:
1054 if (batteryStatus.charge_state > _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id]) {
1055 _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id] = batteryStatus.charge_state;
1056 batteryMessage = tr("battery %1 failed");
1057 }
1058 break;
1059 case MAV_BATTERY_CHARGE_STATE_UNHEALTHY:
1060 if (batteryStatus.charge_state > _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id]) {
1061 _lowestBatteryChargeStateAnnouncedMap[batteryStatus.id] = batteryStatus.charge_state;
1062 batteryMessage = tr("battery %1 unhealthy");
1063 }
1064 break;
1065 }
1066
1067 if (!batteryMessage.isEmpty()) {
1068 QString batteryIdStr("%1");
1070 batteryIdStr = batteryIdStr.arg(batteryStatus.id);
1071 } else {
1072 batteryIdStr = batteryIdStr.arg("");
1073 }
1074 _say(tr("warning"));
1075 _say(QStringLiteral("%1 %2 ").arg(_vehicleIdSpeech()).arg(batteryMessage.arg(batteryIdStr)));
1076 }
1077}
1078
1079void Vehicle::_setHomePosition(QGeoCoordinate& homeCoord)
1080{
1081 if (homeCoord != _homePosition) {
1082 _homePosition = homeCoord;
1083 qCDebug(VehicleLog) << "new home location set at coordinate: " << homeCoord;
1084 emit homePositionChanged(_homePosition);
1085 }
1086}
1087
1088void Vehicle::_handleHomePosition(mavlink_message_t& message)
1089{
1090 mavlink_home_position_t homePos;
1091
1092 mavlink_msg_home_position_decode(&message, &homePos);
1093
1094 QGeoCoordinate newHomePosition (homePos.latitude / 10000000.0,
1095 homePos.longitude / 10000000.0,
1096 homePos.altitude / 1000.0);
1097 _setHomePosition(newHomePosition);
1098}
1099
1100void Vehicle::_updateArmed(bool armed)
1101{
1102 if (_armed != armed) {
1103 _armed = armed;
1104 emit armedChanged(_armed);
1105 // We are transitioning to the armed state, begin tracking trajectory points for the map
1106 if (_armed) {
1107 _trajectoryPoints->start();
1108 _flightTimerStart();
1109 _clearCameraTriggerPoints();
1110 // Reset battery warning
1112 } else {
1113 _trajectoryPoints->stop();
1114 _flightTimerStop();
1115 // Also handle Video Streaming
1116 if(SettingsManager::instance()->videoSettings()->disableWhenDisarmed()->rawValue().toBool()) {
1117 SettingsManager::instance()->videoSettings()->streamEnabled()->setRawValue(false);
1118 VideoManager::instance()->stopVideo();
1119 }
1120 }
1121 }
1122}
1123
1124void Vehicle::_handlePing(LinkInterface* link, mavlink_message_t& message)
1125{
1126 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
1127 if (!sharedLink) {
1128 qCDebug(VehicleLog) << "_handlePing: primary link gone!";
1129 return;
1130 }
1131
1132 mavlink_ping_t ping;
1134
1135 mavlink_msg_ping_decode(&message, &ping);
1136
1137 if ((ping.target_system == 0) && (ping.target_component == 0)) {
1138 // Mavlink defines a ping request as a MSG_ID_PING which contains target_system = 0 and target_component = 0
1139 // So only send a ping response when you receive a valid ping request
1140 mavlink_msg_ping_pack_chan(static_cast<uint8_t>(MAVLinkProtocol::instance()->getSystemId()),
1141 static_cast<uint8_t>(MAVLinkProtocol::getComponentId()),
1142 sharedLink->mavlinkChannel(),
1143 &msg,
1144 ping.time_usec,
1145 ping.seq,
1146 message.sysid,
1147 message.compid);
1148 sendMessageOnLinkThreadSafe(link, msg);
1149 }
1150}
1151
1152void Vehicle::_handleEvent(uint8_t comp_id, std::unique_ptr<events::parser::ParsedEvent> event)
1153{
1154 int severity = -1;
1155 switch (events::externalLogLevel(event->eventData().log_levels)) {
1156 case events::Log::Emergency: severity = MAV_SEVERITY_EMERGENCY; break;
1157 case events::Log::Alert: severity = MAV_SEVERITY_ALERT; break;
1158 case events::Log::Critical: severity = MAV_SEVERITY_CRITICAL; break;
1159 case events::Log::Error: severity = MAV_SEVERITY_ERROR; break;
1160 case events::Log::Warning: severity = MAV_SEVERITY_WARNING; break;
1161 case events::Log::Notice: severity = MAV_SEVERITY_NOTICE; break;
1162 case events::Log::Info: severity = MAV_SEVERITY_INFO; break;
1163 default: break;
1164 }
1165
1166 // handle special groups & protocols
1167 if (event->group() == "health" || event->group() == "arming_check") {
1168 // these are displayed separately
1169 return;
1170 }
1171 if (event->group() == "calibration") {
1172 emit calibrationEventReceived(id(), comp_id, severity,
1173 QSharedPointer<events::parser::ParsedEvent>{new events::parser::ParsedEvent{*event}});
1174 // these are displayed separately
1175 return;
1176 }
1177
1178 // show message according to the log level, don't show unknown event groups (might be part of a new protocol)
1179 if (event->group() == "default" && severity != -1) {
1180 std::string message = event->message();
1181 std::string description = event->description();
1182
1183 if (event->type() == "append_health_and_arming_messages" && event->numArguments() > 0) {
1184 uint32_t customMode = event->argumentValue(0).value.val_uint32_t;
1185 const QSharedPointer<EventHandler>& eventHandler = _events[comp_id];
1186 int modeGroup = eventHandler->getModeGroup(customMode);
1187 std::vector<events::HealthAndArmingChecks::Check> checks = eventHandler->healthAndArmingCheckResults().checks(modeGroup);
1188 QList<std::string> messageChecks;
1189 for (const auto& check : checks) {
1190 if (events::externalLogLevel(check.log_levels) <= events::Log::Warning) {
1191 messageChecks.append(check.message);
1192 }
1193 }
1194 if (messageChecks.empty()) {
1195 // Add all
1196 for (const auto& check : checks) {
1197 messageChecks.append(check.message);
1198 }
1199 }
1200 if (!message.empty() && !messageChecks.empty()) {
1201 message += "\n";
1202 }
1203 if (messageChecks.size() == 1) {
1204 message += messageChecks[0];
1205 } else {
1206 for (const auto& messageCheck : messageChecks) {
1207 message += "- " + messageCheck + "\n";
1208 }
1209 }
1210 }
1211
1212 if (!message.empty()) {
1213 const QString text = QString::fromStdString(message);
1214 // Hack to prevent calibration messages from cluttering things up
1215 if (px4Firmware() && text.startsWith(QStringLiteral("[cal]"))) {
1216 return;
1217 }
1218
1219 m_statusTextHandler->handleHTMLEscapedTextMessage(static_cast<MAV_COMPONENT>(comp_id), static_cast<MAV_SEVERITY>(severity), text, QString::fromStdString(description));
1220 }
1221 }
1222}
1223
1224EventHandler& Vehicle::_eventHandler(uint8_t compid)
1225{
1226 auto eventData = _events.find(compid);
1227 if (eventData == _events.end()) {
1228 // add new component
1229
1230 auto sendRequestEventMessageCB = [this](const mavlink_request_event_t& msg) {
1231 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
1232 if (sharedLink) {
1233 mavlink_message_t message;
1234 mavlink_msg_request_event_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
1236 sharedLink->mavlinkChannel(),
1237 &message,
1238 &msg);
1239 sendMessageOnLinkThreadSafe(sharedLink.get(), message);
1240 }
1241 };
1242
1243 QString profile = "dev"; // TODO: should be configurable
1244
1245 QSharedPointer<EventHandler> eventHandler{new EventHandler(this, profile,
1246 std::bind(&Vehicle::_handleEvent, this, compid, std::placeholders::_1),
1247 sendRequestEventMessageCB,
1248 MAVLinkProtocol::instance()->getSystemId(), MAVLinkProtocol::getComponentId(), _systemID, compid)};
1249 eventData = _events.insert(compid, eventHandler);
1250
1251 // connect health and arming check updates
1252 connect(eventHandler.data(), &EventHandler::healthAndArmingChecksUpdated, this, [compid, this]() {
1253 const QSharedPointer<EventHandler>& evtHandler = _events[compid];
1254 _healthAndArmingCheckReport.update(compid, evtHandler->healthAndArmingCheckResults(),
1255 evtHandler->getModeGroup(_has_custom_mode_user_intention ? _custom_mode_user_intention : _custom_mode));
1256 });
1257 connect(this, &Vehicle::flightModeChanged, this, [compid, this]() {
1258 const QSharedPointer<EventHandler>& evtHandler = _events[compid];
1259 if (evtHandler->healthAndArmingCheckResultsValid()) {
1260 _healthAndArmingCheckReport.update(compid, evtHandler->healthAndArmingCheckResults(),
1261 evtHandler->getModeGroup(_has_custom_mode_user_intention ? _custom_mode_user_intention : _custom_mode));
1262 }
1263 });
1264 }
1265 return *eventData->data();
1266}
1267
1268void Vehicle::setEventsMetadata(uint8_t compid, const QString& metadataJsonFileName)
1269{
1270 _eventHandler(compid).setMetadata(metadataJsonFileName);
1271
1272 // get the mode group for some well-known flight modes
1273 int modeGroups[2]{-1, -1};
1274 const QString modes[2]{_firmwarePlugin->takeOffFlightMode(), _firmwarePlugin->missionFlightMode()};
1275 for (size_t i = 0; i < sizeof(modeGroups)/sizeof(modeGroups[0]); ++i) {
1276 uint8_t base_mode;
1277 uint32_t custom_mode;
1278 if (setFlightModeCustom(modes[i], &base_mode, &custom_mode)) {
1279 modeGroups[i] = _eventHandler(compid).getModeGroup(custom_mode);
1280 if (modeGroups[i] == -1) {
1281 qCDebug(VehicleLog) << "Failed to get mode group for mode" << modes[i] << "(Might not be in metadata)";
1282 }
1283 }
1284 }
1285 _healthAndArmingCheckReport.setModeGroups(modeGroups[0], modeGroups[1]);
1286
1287 // Request arming checks to be reported
1288 sendMavCommand(_defaultComponentId,
1289 MAV_CMD_RUN_PREARM_CHECKS,
1290 false);
1291}
1292
1293void Vehicle::setActuatorsMetadata([[maybe_unused]] uint8_t compid,
1294 const QString &metadataJsonFileName)
1295{
1296 if (!_actuators) {
1297 _actuators = new Actuators(this, this);
1298 }
1299 _actuators->load(metadataJsonFileName);
1300}
1301
1302void Vehicle::_handleHeartbeat(mavlink_message_t& message)
1303{
1304 if (message.compid != _defaultComponentId) {
1305 return;
1306 }
1307
1308 mavlink_heartbeat_t heartbeat;
1309
1310 mavlink_msg_heartbeat_decode(&message, &heartbeat);
1311
1312 bool newArmed = heartbeat.base_mode & MAV_MODE_FLAG_DECODE_POSITION_SAFETY;
1313
1314 // ArduPilot firmare has a strange case when ARMING_REQUIRE=0. This means the vehicle is always armed but the motors are not
1315 // really powered up until the safety button is pressed. Because of this we can't depend on the heartbeat to tell us the true
1316 // armed (and dangerous) state. We must instead rely on SYS_STATUS telling us that the motors are enabled.
1317 if (apmFirmware()) {
1318 if (!_apmArmingNotRequired() || !(_onboardControlSensorsPresent & MAV_SYS_STATUS_SENSOR_MOTOR_OUTPUTS)) {
1319 // If ARMING_REQUIRE!=0 or we haven't seen motor output status yet we use the hearbeat info for armed
1320 _updateArmed(newArmed);
1321 }
1322 } else {
1323 // Non-ArduPilot always updates from armed state in heartbeat
1324 _updateArmed(newArmed);
1325 }
1326
1327 if (heartbeat.base_mode != _base_mode || heartbeat.custom_mode != _custom_mode) {
1328 QString previousFlightMode;
1329 if (_base_mode != 0 || _custom_mode != 0){
1330 // Vehicle is initialized with _base_mode=0 and _custom_mode=0. Don't pass this to flightMode() since it will complain about
1331 // bad modes while unit testing.
1332 previousFlightMode = flightMode();
1333 }
1334 _base_mode = heartbeat.base_mode;
1335 _custom_mode = heartbeat.custom_mode;
1336 if (previousFlightMode != flightMode()) {
1338 }
1339 }
1340}
1341
1342void Vehicle::_handleCurrentMode(mavlink_message_t& message)
1343{
1344 mavlink_current_mode_t currentMode;
1345 mavlink_msg_current_mode_decode(&message, &currentMode);
1346 if (currentMode.intended_custom_mode != 0) { // 0 == unknown/not supplied
1347 _has_custom_mode_user_intention = true;
1348 QString previousFlightMode = flightMode();
1349 bool changed = _custom_mode_user_intention != currentMode.intended_custom_mode;
1350 _custom_mode_user_intention = currentMode.intended_custom_mode;
1351 if (changed && previousFlightMode != flightMode()) {
1353 }
1354 }
1355}
1356
1357void Vehicle::_handleRadioStatus(mavlink_message_t& message)
1358{
1359
1360 //-- Process telemetry status message
1361 mavlink_radio_status_t rstatus;
1362 mavlink_msg_radio_status_decode(&message, &rstatus);
1363
1364 int rssi = rstatus.rssi;
1365 int remrssi = rstatus.remrssi;
1366 int lnoise = (int)(int8_t)rstatus.noise;
1367 int rnoise = (int)(int8_t)rstatus.remnoise;
1368 //-- 3DR Si1k radio needs rssi fields to be converted to dBm
1369 if (message.sysid == '3' && message.compid == 'D') {
1370 /* Per the Si1K datasheet figure 23.25 and SI AN474 code
1371 * samples the relationship between the RSSI register
1372 * and received power is as follows:
1373 *
1374 * 10
1375 * inputPower = rssi * ------ 127
1376 * 19
1377 *
1378 * Additionally limit to the only realistic range [-120,0] dBm
1379 */
1380 rssi = qMin(qMax(qRound(static_cast<qreal>(rssi) / 1.9 - 127.0), - 120), 0);
1381 remrssi = qMin(qMax(qRound(static_cast<qreal>(remrssi) / 1.9 - 127.0), - 120), 0);
1382 } else {
1383 rssi = (int)(int8_t)rstatus.rssi;
1384 remrssi = (int)(int8_t)rstatus.remrssi;
1385 }
1386 //-- Check for changes
1387 if(_telemetryLRSSI != rssi) {
1388 _telemetryLRSSI = rssi;
1389 emit telemetryLRSSIChanged(_telemetryLRSSI);
1390 }
1391 if(_telemetryRRSSI != remrssi) {
1392 _telemetryRRSSI = remrssi;
1393 emit telemetryRRSSIChanged(_telemetryRRSSI);
1394 }
1395 if(_telemetryRXErrors != rstatus.rxerrors) {
1396 _telemetryRXErrors = rstatus.rxerrors;
1397 emit telemetryRXErrorsChanged(_telemetryRXErrors);
1398 }
1399 if(_telemetryFixed != rstatus.fixed) {
1400 _telemetryFixed = rstatus.fixed;
1401 emit telemetryFixedChanged(_telemetryFixed);
1402 }
1403 if(_telemetryTXBuffer != rstatus.txbuf) {
1404 _telemetryTXBuffer = rstatus.txbuf;
1405 emit telemetryTXBufferChanged(_telemetryTXBuffer);
1406 }
1407 if(_telemetryLNoise != lnoise) {
1408 _telemetryLNoise = lnoise;
1409 emit telemetryLNoiseChanged(_telemetryLNoise);
1410 }
1411 if(_telemetryRNoise != rnoise) {
1412 _telemetryRNoise = rnoise;
1413 emit telemetryRNoiseChanged(_telemetryRNoise);
1414 }
1415}
1416
1417void Vehicle::_handleRCChannels(mavlink_message_t& message)
1418{
1419 mavlink_rc_channels_t channels;
1420
1421 mavlink_msg_rc_channels_decode(&message, &channels);
1422
1423 QVector<uint16_t> rawChannelValues({
1424 channels.chan1_raw,
1425 channels.chan2_raw,
1426 channels.chan3_raw,
1427 channels.chan4_raw,
1428 channels.chan5_raw,
1429 channels.chan6_raw,
1430 channels.chan7_raw,
1431 channels.chan8_raw,
1432 channels.chan9_raw,
1433 channels.chan10_raw,
1434 channels.chan11_raw,
1435 channels.chan12_raw,
1436 channels.chan13_raw,
1437 channels.chan14_raw,
1438 channels.chan15_raw,
1439 channels.chan16_raw,
1440 channels.chan17_raw,
1441 channels.chan18_raw,
1442 });
1443
1444 // The internals of radio calibration can ony deal with contiguous channels (other stuff as well!)
1445 int validChannelCount = 0;
1446 int firstUnusedChannelIndex = -1;
1447 for (int i=0; i<rawChannelValues.size(); i++) {
1448 if (rawChannelValues[i] != UINT16_MAX) {
1449 validChannelCount++;
1450 } else if (firstUnusedChannelIndex == -1) {
1451 firstUnusedChannelIndex = i;
1452 }
1453 }
1454 if (firstUnusedChannelIndex != -1 && firstUnusedChannelIndex != validChannelCount) {
1455 qCWarning(VehicleLog) << "Non-contiguous RC channels detected. Not publishing data from RC_CHANNELS.";
1456 return;
1457 }
1458
1459 QVector<int> channelValues(validChannelCount);
1460 for (int channelIndex = 0; channelIndex < validChannelCount; ++channelIndex) {
1461 int channelValue = rawChannelValues[channelIndex];
1462 // Radio cal can only handle pwm values in the 1000:2000 range, so constrain to that
1463 channelValues[channelIndex] = std::min(std::max(channelValue, 1000), 2000);
1464 }
1465
1466 emit remoteControlRSSIChanged(channels.rssi);
1467 emit rcChannelsChanged(channelValues);
1468}
1469
1471{
1472 if (!link->isConnected()) {
1473 qCDebug(VehicleLog) << "sendMessageOnLinkThreadSafe" << link << "not connected!";
1474 return false;
1475 }
1476
1477 // Give the plugin a chance to adjust
1478 _firmwarePlugin->adjustOutgoingMavlinkMessageThreadSafe(this, link, &message);
1479
1480 // Write message into buffer, prepending start sign
1481 uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
1482 int len = mavlink_msg_to_send_buffer(buffer, &message);
1483
1484 link->writeBytesThreadSafe((const char*)buffer, len);
1485 _messagesSent++;
1486 emit messagesSentChanged();
1487
1488 return true;
1489}
1490
1492{
1493 uint8_t frameType = 0;
1494 if (_vehicleType == MAV_TYPE_SUBMARINE) {
1495 frameType = parameterManager()->getParameter(_compID, "FRAME_CONFIG")->rawValue().toInt();
1496 }
1497 return QGCMAVLink::motorCount(_vehicleType, frameType);
1498}
1499
1501{
1502 return _firmwarePlugin->multiRotorCoaxialMotors(this);
1503}
1504
1506{
1507 return _firmwarePlugin->multiRotorXConfig(this);
1508}
1509
1510void Vehicle::_activeVehicleChanged(Vehicle *newActiveVehicle)
1511{
1512 _isActiveVehicle = newActiveVehicle == this;
1513}
1514
1516{
1517 return _homePosition;
1518}
1519
1520void Vehicle::setArmed(bool armed, bool showError)
1521{
1522 // We specifically use COMMAND_LONG:MAV_CMD_COMPONENT_ARM_DISARM since it is supported by more flight stacks.
1523 sendMavCommand(_defaultComponentId,
1524 MAV_CMD_COMPONENT_ARM_DISARM,
1525 showError,
1526 armed ? 1.0f : 0.0f);
1527}
1528
1530{
1531 sendMavCommand(_defaultComponentId,
1532 MAV_CMD_COMPONENT_ARM_DISARM,
1533 true, // show error if fails
1534 1.0f, // arm
1535 2989); // force arm
1536}
1537
1539{
1540 return _firmwarePlugin->isCapable(this, FirmwarePlugin::SetFlightModeCapability);
1541}
1542
1544{
1545 QStringList flightModes = _firmwarePlugin->flightModes(this);
1546 return flightModes;
1547}
1548
1549QString Vehicle::flightMode() const
1550{
1551 return _firmwarePlugin->flightMode(_base_mode, _custom_mode);
1552}
1553
1554bool Vehicle::setFlightModeCustom(const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode)
1555{
1556 return _firmwarePlugin->setFlightMode(flightMode, base_mode, custom_mode);
1557}
1558
1559void Vehicle::setFlightMode(const QString& flightMode)
1560{
1561 uint8_t base_mode;
1562 uint32_t custom_mode;
1563
1564 if (setFlightModeCustom(flightMode, &base_mode, &custom_mode)) {
1565 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
1566 if (!sharedLink) {
1567 qCDebug(VehicleLog) << "setFlightMode: primary link gone!";
1568 return;
1569 }
1570
1571 uint8_t newBaseMode = _base_mode & ~MAV_MODE_FLAG_DECODE_POSITION_CUSTOM_MODE;
1572
1573 // setFlightMode will only set MAV_MODE_FLAG_CUSTOM_MODE_ENABLED in base_mode, we need to move back in the existing
1574 // states.
1575 newBaseMode |= base_mode;
1576
1577 if (_firmwarePlugin->MAV_CMD_DO_SET_MODE_is_supported()) {
1579 MAV_CMD_DO_SET_MODE,
1580 true, // show error if fails
1581 MAV_MODE_FLAG_CUSTOM_MODE_ENABLED,
1582 custom_mode);
1583 } else {
1585 mavlink_msg_set_mode_pack_chan(MAVLinkProtocol::instance()->getSystemId(),
1587 sharedLink->mavlinkChannel(),
1588 &msg,
1589 id(),
1590 newBaseMode,
1591 custom_mode);
1592 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
1593 }
1594 } else {
1595 qCWarning(VehicleLog) << "FirmwarePlugin::setFlightMode failed, flightMode:" << flightMode;
1596 }
1597}
1598
1599#if 0
1600QVariantList Vehicle::links() const {
1601 QVariantList ret;
1602
1603 for( const auto &item: _links )
1604 ret << QVariant::fromValue(item);
1605
1606 return ret;
1607}
1608#endif
1609
1610void Vehicle::requestDataStream(MAV_DATA_STREAM stream, uint16_t rate, bool sendMultiple)
1611{
1612 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
1613 if (!sharedLink) {
1614 qCDebug(VehicleLog) << "requestDataStream: primary link gone!";
1615 return;
1616 }
1617
1619 mavlink_request_data_stream_t dataStream;
1620
1621 memset(&dataStream, 0, sizeof(dataStream));
1622
1623 dataStream.req_stream_id = stream;
1624 dataStream.req_message_rate = rate;
1625 dataStream.start_stop = 1; // start
1626 dataStream.target_system = id();
1627 dataStream.target_component = _defaultComponentId;
1628
1629 mavlink_msg_request_data_stream_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
1631 sharedLink->mavlinkChannel(),
1632 &msg,
1633 &dataStream);
1634
1635 if (sendMultiple) {
1636 // We use sendMessageMultiple since we really want these to make it to the vehicle
1638 } else {
1639 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
1640 }
1641}
1642
1643void Vehicle::_sendMessageMultipleNext()
1644{
1645 if (_nextSendMessageMultipleIndex < _sendMessageMultipleList.count()) {
1646 uint32_t msgId = _sendMessageMultipleList[_nextSendMessageMultipleIndex].message.msgid;
1647 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(msgId);
1648 QString msgName = info ? info->name : QString::number(msgId);
1649 qCDebug(VehicleLog) << "_sendMessageMultipleNext:" << msgName;
1650
1651 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
1652 if (sharedLink) {
1653 sendMessageOnLinkThreadSafe(sharedLink.get(), _sendMessageMultipleList[_nextSendMessageMultipleIndex].message);
1654 }
1655
1656 if (--_sendMessageMultipleList[_nextSendMessageMultipleIndex].retryCount <= 0) {
1657 _sendMessageMultipleList.removeAt(_nextSendMessageMultipleIndex);
1658 } else {
1659 _nextSendMessageMultipleIndex++;
1660 }
1661 }
1662
1663 if (_nextSendMessageMultipleIndex >= _sendMessageMultipleList.count()) {
1664 _nextSendMessageMultipleIndex = 0;
1665 }
1666}
1667
1669{
1670 SendMessageMultipleInfo_t info;
1671
1672 info.message = message;
1673 info.retryCount = _sendMessageMultipleRetries;
1674
1675 _sendMessageMultipleList.append(info);
1676}
1677
1678void Vehicle::_missionManagerError(int errorCode, const QString& errorMsg)
1679{
1680 Q_UNUSED(errorCode);
1681 qgcApp()->showAppMessage(tr("Mission transfer failed. Error: %1").arg(errorMsg));
1682}
1683
1684void Vehicle::_geoFenceManagerError(int errorCode, const QString& errorMsg)
1685{
1686 Q_UNUSED(errorCode);
1687 qgcApp()->showAppMessage(tr("GeoFence transfer failed. Error: %1").arg(errorMsg));
1688}
1689
1690void Vehicle::_rallyPointManagerError(int errorCode, const QString& errorMsg)
1691{
1692 Q_UNUSED(errorCode);
1693 qgcApp()->showAppMessage(tr("Rally Point transfer failed. Error: %1").arg(errorMsg));
1694}
1695
1696void Vehicle::_clearCameraTriggerPoints()
1697{
1698 _cameraTriggerPoints.clearAndDeleteContents();
1699}
1700
1701void Vehicle::_flightTimerStart()
1702{
1703 _flightTimer.start();
1704 _flightTimeUpdater.start();
1705 _flightDistanceFact.setRawValue(0);
1706 _flightTimeFact.setRawValue(0);
1707}
1708
1709void Vehicle::_flightTimerStop()
1710{
1711 _flightTimeUpdater.stop();
1712}
1713
1714void Vehicle::_updateFlightTime()
1715{
1716 _flightTimeFact.setRawValue((double)_flightTimer.elapsed() / 1000.0);
1717}
1718
1719void Vehicle::_gotProgressUpdate(float progressValue)
1720{
1721 if (sender() != _initialConnectStateMachine && _initialConnectStateMachine->active()) {
1722 return;
1723 }
1724 if (sender() == _initialConnectStateMachine && !_initialConnectStateMachine->active()) {
1725 progressValue = 0.f;
1726 }
1727 _loadProgress = progressValue;
1728 emit loadProgressChanged(progressValue);
1729}
1730
1731void Vehicle::_firstMissionLoadComplete()
1732{
1733 disconnect(_missionManager, &MissionManager::newMissionItemsAvailable, this, &Vehicle::_firstMissionLoadComplete);
1734}
1735
1736void Vehicle::_firstGeoFenceLoadComplete()
1737{
1738 disconnect(_geoFenceManager, &GeoFenceManager::loadComplete, this, &Vehicle::_firstGeoFenceLoadComplete);
1739}
1740
1741void Vehicle::_firstRallyPointLoadComplete()
1742{
1743 disconnect(_rallyPointManager, &RallyPointManager::loadComplete, this, &Vehicle::_firstRallyPointLoadComplete);
1744 _initialPlanRequestComplete = true;
1746}
1747
1748void Vehicle::_parametersReady(bool parametersReady)
1749{
1750 qCDebug(VehicleLog) << "_parametersReady" << parametersReady;
1751
1752 // Try to set current unix time to the vehicle
1753 _sendQGCTimeToVehicle();
1754 // Send time twice, more likely to get to the vehicle on a noisy link
1755 _sendQGCTimeToVehicle();
1756 if (parametersReady) {
1757 disconnect(_parameterManager, &ParameterManager::parametersReadyChanged, this, &Vehicle::_parametersReady);
1758 _setupAutoDisarmSignalling();
1759 }
1760
1763
1764 emit haveMRSpeedLimChanged();
1765 emit haveFWSpeedLimChanged();
1766}
1767
1768void Vehicle::_sendQGCTimeToVehicle()
1769{
1770 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
1771 if (!sharedLink) {
1772 qCDebug(VehicleLog) << "_sendQGCTimeToVehicle: primary link gone!";
1773 return;
1774 }
1775
1777 mavlink_system_time_t cmd;
1778
1779 // Timestamp of the master clock in microseconds since UNIX epoch.
1780 cmd.time_unix_usec = QDateTime::currentDateTime().currentMSecsSinceEpoch()*1000;
1781 // Timestamp of the component clock since boot time in milliseconds (Not necessary).
1782 cmd.time_boot_ms = 0;
1783 mavlink_msg_system_time_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
1785 sharedLink->mavlinkChannel(),
1786 &msg,
1787 &cmd);
1788
1789 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
1790}
1791
1792void Vehicle::_remoteControlRSSIChanged(uint8_t rssi)
1793{
1794 //-- 0 <= rssi <= 100 - 255 means "invalid/unknown"
1795 if(rssi > 100) { // Anything over 100 doesn't make sense
1796 if(_rcRSSI != 255) {
1797 _rcRSSI = 255;
1798 emit rcRSSIChanged(_rcRSSI);
1799 }
1800 return;
1801 }
1802 //-- Initialize it
1803 if(_rcRSSIstore == 255.) {
1804 _rcRSSIstore = (double)rssi;
1805 }
1806 // Low pass to git rid of jitter
1807 _rcRSSIstore = (_rcRSSIstore * 0.9f) + ((float)rssi * 0.1);
1808 uint8_t filteredRSSI = (uint8_t)ceil(_rcRSSIstore);
1809 if(_rcRSSIstore < 0.1) {
1810 filteredRSSI = 0;
1811 }
1812 if(_rcRSSI != filteredRSSI) {
1813 _rcRSSI = filteredRSSI;
1814 emit rcRSSIChanged(_rcRSSI);
1815 }
1816}
1817
1818void Vehicle::virtualTabletJoystickValue(double roll, double pitch, double yaw, double thrust)
1819{
1820 // The following if statement prevents the virtualTabletJoystick from sending values if the standard joystick is enabled
1821 bool isActiveVehicle = (MultiVehicleManager::instance()->activeVehicle() == this);
1822 bool joystickEnabled = isActiveVehicle && JoystickManager::instance()->activeJoystickEnabledForActiveVehicle();
1823 if (!joystickEnabled) {
1825 static_cast<float>(roll),
1826 static_cast<float>(pitch),
1827 static_cast<float>(yaw),
1828 static_cast<float>(thrust),
1829 0, 0, // buttons
1830 NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN); // extension values
1831 }
1832}
1833
1834void Vehicle::_say(const QString& text)
1835{
1836 AudioOutput::instance()->say(text.toLower());
1837}
1838
1840{
1842}
1843
1845{
1847}
1848
1849bool Vehicle::rover() const
1850{
1852}
1853
1854bool Vehicle::sub() const
1855{
1857}
1858
1860{
1862}
1863
1865{
1867}
1868
1869bool Vehicle::vtol() const
1870{
1872}
1873
1875{
1876 return QGCMAVLink::mavTypeToString(_vehicleType);
1877}
1878
1883
1885QString Vehicle::_vehicleIdSpeech()
1886{
1887 if (MultiVehicleManager::instance()->vehicles()->count() > 1) {
1888 return tr("Vehicle %1 ").arg(id());
1889 } else {
1890 return QString();
1891 }
1892}
1893
1894void Vehicle::_handleFlightModeChanged(const QString& flightMode)
1895{
1896 _say(tr("%1 %2 flight mode").arg(_vehicleIdSpeech()).arg(flightMode));
1897 emit guidedModeChanged(_firmwarePlugin->isGuidedMode(this));
1898}
1899
1900void Vehicle::_announceArmedChanged(bool armed)
1901{
1902 _say(QString("%1 %2").arg(_vehicleIdSpeech()).arg(armed ? tr("armed") : tr("disarmed")));
1903 if(armed) {
1904 //-- Keep track of armed coordinates
1905 _armedPosition = _coordinate;
1906 emit armedPositionChanged();
1907 }
1908}
1909
1910void Vehicle::_setFlying(bool flying)
1911{
1912 if (_flying != flying) {
1913 _flying = flying;
1914 emit flyingChanged(flying);
1915 }
1916}
1917
1918void Vehicle::_setLanding(bool landing)
1919{
1920 if (armed() && _landing != landing) {
1921 _landing = landing;
1922 emit landingChanged(landing);
1923 }
1924}
1925
1927{
1928 return _firmwarePlugin->gotoFlightMode();
1929}
1930
1931void Vehicle::guidedModeRTL(bool smartRTL)
1932{
1933 if (!_vehicleSupports->guidedMode()) {
1935 return;
1936 }
1937 _firmwarePlugin->guidedModeRTL(this, smartRTL);
1938}
1939
1941{
1942 if (!_vehicleSupports->guidedMode()) {
1944 return;
1945 }
1946 _firmwarePlugin->guidedModeLand(this);
1947}
1948
1949void Vehicle::guidedModeTakeoff(double altitudeRelative)
1950{
1951 if (!_vehicleSupports->guidedMode()) {
1953 return;
1954 }
1955 _firmwarePlugin->guidedModeTakeoff(this, altitudeRelative);
1956}
1957
1959{
1960 return _firmwarePlugin->minimumTakeoffAltitudeMeters(this);
1961}
1962
1964{
1965 return _firmwarePlugin->maximumHorizontalSpeedMultirotor(this);
1966}
1967
1968
1970{
1971 return _firmwarePlugin->maximumEquivalentAirspeed(this);
1972}
1973
1974
1976{
1977 return _firmwarePlugin->minimumEquivalentAirspeed(this);
1978}
1979
1981{
1982 return _firmwarePlugin->hasGripper(this);
1983}
1984
1986{
1987 _firmwarePlugin->startTakeoff(this);
1988}
1989
1990
1992{
1993 _firmwarePlugin->startMission(this);
1994}
1995
1996void Vehicle::guidedModeGotoLocation(const QGeoCoordinate& gotoCoord, double forwardFlightLoiterRadius)
1997{
1998 if (!_vehicleSupports->guidedMode()) {
2000 return;
2001 }
2002 if (!coordinate().isValid()) {
2003 return;
2004 }
2005 double maxDistance = SettingsManager::instance()->flyViewSettings()->maxGoToLocationDistance()->rawValue().toDouble();
2006 if (coordinate().distanceTo(gotoCoord) > maxDistance) {
2007 qgcApp()->showAppMessage(QString("New location is too far. Must be less than %1 %2.").arg(qRound(FactMetaData::metersToAppSettingsHorizontalDistanceUnits(maxDistance).toDouble())).arg(FactMetaData::appSettingsHorizontalDistanceUnitsString()));
2008 return;
2009 }
2010 _firmwarePlugin->guidedModeGotoLocation(this, gotoCoord, forwardFlightLoiterRadius);
2011}
2012
2013void Vehicle::guidedModeChangeAltitude(double altitudeChange, bool pauseVehicle)
2014{
2015 if (!_vehicleSupports->guidedMode()) {
2017 return;
2018 }
2019 _firmwarePlugin->guidedModeChangeAltitude(this, altitudeChange, pauseVehicle);
2020}
2021
2022void
2024{
2025 if (!_vehicleSupports->guidedMode()) {
2027 return;
2028 }
2029 _firmwarePlugin->guidedModeChangeGroundSpeedMetersSecond(this, groundspeed);
2030}
2031
2032void
2034{
2035 if (!_vehicleSupports->guidedMode()) {
2037 return;
2038 }
2039 _firmwarePlugin->guidedModeChangeEquivalentAirspeedMetersSecond(this, airspeed);
2040}
2041
2042void Vehicle::guidedModeOrbit(const QGeoCoordinate& centerCoord, double radius, double amslAltitude)
2043{
2044 if (!_vehicleSupports->orbitMode()) {
2045 qgcApp()->showAppMessage(QStringLiteral("Orbit mode not supported by Vehicle."));
2046 return;
2047 }
2048 if (capabilityBits() & MAV_PROTOCOL_CAPABILITY_COMMAND_INT) {
2051 MAV_CMD_DO_ORBIT,
2052 MAV_FRAME_GLOBAL,
2053 true, // show error if fails
2054 static_cast<float>(radius),
2055 static_cast<float>(qQNaN()), // Use default velocity
2056 static_cast<float>(ORBIT_YAW_BEHAVIOUR_UNCHANGED), // Use current or vehicle default yaw behavior
2057 static_cast<float>(qQNaN()), // Use vehicle default num of orbits behavior
2058 centerCoord.latitude(), centerCoord.longitude(), static_cast<float>(amslAltitude));
2059 } else {
2062 MAV_CMD_DO_ORBIT,
2063 true, // show error if fails
2064 static_cast<float>(radius),
2065 static_cast<float>(qQNaN()), // Use default velocity
2066 static_cast<float>(ORBIT_YAW_BEHAVIOUR_UNCHANGED), // Use current or vehicle default yaw behavior
2067 static_cast<float>(qQNaN()), // Use vehicle default num of orbits behavior
2068 static_cast<float>(centerCoord.latitude()),
2069 static_cast<float>(centerCoord.longitude()),
2070 static_cast<float>(amslAltitude));
2071 }
2072}
2073
2074void Vehicle::guidedModeROI(const QGeoCoordinate& centerCoord)
2075{
2076 if (!centerCoord.isValid()) {
2077 return;
2078 }
2079 if (!_vehicleSupports->roiMode()) {
2080 qgcApp()->showAppMessage(QStringLiteral("ROI mode not supported by Vehicle."));
2081 return;
2082 }
2083
2084 if (px4Firmware()) {
2085 // PX4 ignores the coordinate frame in COMMAND_INT and treats the altitude as AMSL.
2086 // The map click provides no altitude, so we query terrain to get a proper AMSL altitude.
2088 disconnect(_roiTerrainAtCoordinateQuery, &TerrainAtCoordinateQuery::terrainDataReceived, this, &Vehicle::_roiTerrainReceived);
2090 }
2091
2092 _roiCoordinate = centerCoord;
2093
2094 _roiTerrainAtCoordinateQuery = new TerrainAtCoordinateQuery(true /* autoDelete */);
2095 connect(_roiTerrainAtCoordinateQuery, &TerrainAtCoordinateQuery::terrainDataReceived, this, &Vehicle::_roiTerrainReceived);
2096 QList<QGeoCoordinate> rgCoord;
2097 rgCoord.append(centerCoord);
2099 } else {
2100 // ArduPilot handles MAV_FRAME_GLOBAL_RELATIVE_ALT correctly, so altitude 0 relative to
2101 // home is a reasonable default for a map click with no altitude info.
2102 // Sanity check Ardupilot. Max altitude processed is 83000
2103 if ((centerCoord.altitude() >= 83000) || (centerCoord.altitude() <= -83000)) {
2104 return;
2105 }
2106 _sendROICommand(centerCoord, MAV_FRAME_GLOBAL_RELATIVE_ALT, static_cast<float>(centerCoord.altitude()));
2107 }
2108
2109 // This is picked by qml to display coordinate over map
2110 emit roiCoordChanged(centerCoord);
2111}
2112
2113void Vehicle::_roiTerrainReceived(bool success, QList<double> heights)
2114{
2116
2117 if (!_roiCoordinate.isValid()) {
2118 return;
2119 }
2120
2121 float roiAltitude;
2122 if (success) {
2123 roiAltitude = static_cast<float>(heights[0]);
2124 } else {
2125 qCDebug(VehicleLog) << "_roiTerrainReceived: terrain query failed, falling back to home altitude";
2126 roiAltitude = static_cast<float>(_homePosition.altitude());
2127 }
2128
2129 qCDebug(VehicleLog) << "guidedModeROI: lat" << _roiCoordinate.latitude() << "lon" << _roiCoordinate.longitude() << "terrainAltAMSL" << roiAltitude << "success" << success;
2130
2131 _sendROICommand(_roiCoordinate, MAV_FRAME_GLOBAL, roiAltitude);
2132
2133 _roiCoordinate = QGeoCoordinate();
2134}
2135
2136void Vehicle::_sendROICommand(const QGeoCoordinate& coord, MAV_FRAME frame, float altitude)
2137{
2138 if (capabilityBits() & MAV_PROTOCOL_CAPABILITY_COMMAND_INT) {
2141 MAV_CMD_DO_SET_ROI_LOCATION,
2142 frame,
2143 true, // show error if fails
2144 static_cast<float>(qQNaN()), // Empty
2145 static_cast<float>(qQNaN()), // Empty
2146 static_cast<float>(qQNaN()), // Empty
2147 static_cast<float>(qQNaN()), // Empty
2148 coord.latitude(),
2149 coord.longitude(),
2150 altitude);
2151 } else {
2154 MAV_CMD_DO_SET_ROI_LOCATION,
2155 true, // show error if fails
2156 static_cast<float>(qQNaN()), // Empty
2157 static_cast<float>(qQNaN()), // Empty
2158 static_cast<float>(qQNaN()), // Empty
2159 static_cast<float>(qQNaN()), // Empty
2160 static_cast<float>(coord.latitude()),
2161 static_cast<float>(coord.longitude()),
2162 altitude);
2163 }
2164}
2165
2167{
2168 if (!_vehicleSupports->roiMode()) {
2169 qgcApp()->showAppMessage(QStringLiteral("ROI mode not supported by Vehicle."));
2170 return;
2171 }
2172 if (capabilityBits() & MAV_PROTOCOL_CAPABILITY_COMMAND_INT) {
2175 MAV_CMD_DO_SET_ROI_NONE,
2176 MAV_FRAME_GLOBAL,
2177 true, // show error if fails
2178 static_cast<float>(qQNaN()), // Empty
2179 static_cast<float>(qQNaN()), // Empty
2180 static_cast<float>(qQNaN()), // Empty
2181 static_cast<float>(qQNaN()), // Empty
2182 static_cast<double>(qQNaN()), // Empty
2183 static_cast<double>(qQNaN()), // Empty
2184 static_cast<float>(qQNaN())); // Empty
2185 } else {
2188 MAV_CMD_DO_SET_ROI_NONE,
2189 true, // show error if fails
2190 static_cast<float>(qQNaN()), // Empty
2191 static_cast<float>(qQNaN()), // Empty
2192 static_cast<float>(qQNaN()), // Empty
2193 static_cast<float>(qQNaN()), // Empty
2194 static_cast<float>(qQNaN()), // Empty
2195 static_cast<float>(qQNaN()), // Empty
2196 static_cast<float>(qQNaN())); // Empty
2197 }
2198}
2199
2200void Vehicle::guidedModeChangeHeading(const QGeoCoordinate &headingCoord)
2201{
2202 if (!_vehicleSupports->changeHeading()) {
2203 qgcApp()->showAppMessage(tr("Change Heading not supported by Vehicle."));
2204 return;
2205 }
2206
2207 _firmwarePlugin->guidedModeChangeHeading(this, headingCoord);
2208}
2209
2211{
2212 if (!_vehicleSupports->pauseVehicle()) {
2213 qgcApp()->showAppMessage(QStringLiteral("Pause not supported by vehicle."));
2214 return;
2215 }
2216 _firmwarePlugin->pauseVehicle(this);
2217}
2218
2219void Vehicle::abortLanding(double climbOutAltitude)
2220{
2223 MAV_CMD_DO_GO_AROUND,
2224 true, // show error if fails
2225 static_cast<float>(climbOutAltitude));
2226}
2227
2229{
2230 return _firmwarePlugin->isGuidedMode(this);
2231}
2232
2233void Vehicle::setGuidedMode(bool guidedMode)
2234{
2235 return _firmwarePlugin->setGuidedMode(this, guidedMode);
2236}
2237
2239{
2240 return fixedWing() || _vtolInFwdFlight;
2241}
2242
2243
2245{
2247 _defaultComponentId,
2248 MAV_CMD_COMPONENT_ARM_DISARM,
2249 true, // show error if fails
2250 0.0f,
2251 21196.0f); // Magic number for emergency stop
2252}
2253
2255{
2258 MAV_CMD_AIRFRAME_CONFIGURATION,
2259 true, // show error if fails
2260 -1.0f, // all gears
2261 0.0f); // down
2262}
2263
2265{
2268 MAV_CMD_AIRFRAME_CONFIGURATION,
2269 true, // show error if fails
2270 -1.0f, // all gears
2271 1.0f); // up
2272}
2273
2275{
2276 if (!_firmwarePlugin->sendHomePositionToVehicle()) {
2277 seq--;
2278 }
2279
2280 // send the mavlink command (created in Jan 2019)
2282 [this,seq]() { // lambda function which uses the deprecated mission_set_current
2283 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
2284 if (!sharedLink) {
2285 qCDebug(VehicleLog) << "setCurrentMissionSequence: primary link gone!";
2286 return;
2287 }
2288
2290
2291 // send mavlink message (deprecated since Aug 2022).
2292 mavlink_msg_mission_set_current_pack_chan(
2293 static_cast<uint8_t>(MAVLinkProtocol::instance()->getSystemId()),
2294 static_cast<uint8_t>(MAVLinkProtocol::getComponentId()),
2295 sharedLink->mavlinkChannel(),
2296 &msg,
2297 static_cast<uint8_t>(id()),
2298 _compID,
2299 static_cast<uint16_t>(seq));
2300 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
2301 },
2302 static_cast<uint8_t>(defaultComponentId()),
2303 MAV_CMD_DO_SET_MISSION_CURRENT,
2304 true, // showError
2305 static_cast<uint16_t>(seq)
2306 );
2307}
2308
2309void Vehicle::sendMavCommand(int compId, MAV_CMD command, bool showError, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
2310{
2311 _sendMavCommandWorker(false, // commandInt
2312 showError,
2313 nullptr, // no handlers
2314 compId,
2315 command,
2316 MAV_FRAME_GLOBAL,
2317 param1, param2, param3, param4, param5, param6, param7);
2318}
2319
2320void Vehicle::sendMavCommandDelayed(int compId, MAV_CMD command, bool showError, int milliseconds, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
2321{
2322 QTimer::singleShot(milliseconds, this, [=, this] { sendMavCommand(compId, command, showError, param1, param2, param3, param4, param5, param6, param7); });
2323}
2324
2325void Vehicle::sendCommand(int compId, int command, bool showError, double param1, double param2, double param3, double param4, double param5, double param6, double param7)
2326{
2328 compId, static_cast<MAV_CMD>(command),
2329 showError,
2330 static_cast<float>(param1),
2331 static_cast<float>(param2),
2332 static_cast<float>(param3),
2333 static_cast<float>(param4),
2334 static_cast<float>(param5),
2335 static_cast<float>(param6),
2336 static_cast<float>(param7));
2337}
2338
2339void Vehicle::sendMavCommandWithHandler(const MavCmdAckHandlerInfo_t* ackHandlerInfo, int compId, MAV_CMD command, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
2340{
2341 _sendMavCommandWorker(false, // commandInt
2342 false, // showError
2343 ackHandlerInfo,
2344 compId,
2345 command,
2346 MAV_FRAME_GLOBAL,
2347 param1, param2, param3, param4, param5, param6, param7);
2348}
2349
2350void Vehicle::sendMavCommandInt(int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
2351{
2352 _sendMavCommandWorker(true, // commandInt
2353 showError,
2354 nullptr, // no handlers
2355 compId,
2356 command,
2357 frame,
2358 param1, param2, param3, param4, param5, param6, param7);
2359}
2360
2361void Vehicle::sendMavCommandIntWithHandler(const MavCmdAckHandlerInfo_t* ackHandlerInfo, int compId, MAV_CMD command, MAV_FRAME frame, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
2362{
2363 _sendMavCommandWorker(true, // commandInt
2364 false, // showError
2365 ackHandlerInfo,
2366 compId,
2367 command,
2368 frame,
2369 param1, param2, param3, param4, param5, param6, param7);
2370}
2371
2377
2378static void _sendMavCommandWithLambdaFallbackHandler(void* resultHandlerData, int /*compId*/, const mavlink_command_ack_t& ack, Vehicle::MavCmdResultFailureCode_t /*failureCode*/)
2379{
2380 auto* data = (_sendMavCommandWithLambdaFallbackHandlerData*)resultHandlerData;
2381 auto* vehicle = data->vehicle;
2382 auto* instanceData = vehicle->firmwarePluginInstanceData();
2383
2384 switch (ack.result) {
2385 case MAV_RESULT_ACCEPTED:
2387 break;
2388 case MAV_RESULT_UNSUPPORTED:
2389 instanceData->setCommandSupported(MAV_CMD(ack.command), FirmwarePluginInstanceData::CommandSupportedResult::UNSUPPORTED);
2390 // call the "unsupported" lambda:
2391 data->unsupported_lambda();
2392 break;
2393 default:
2394 if (data->showError) {
2396 }
2397 break;
2398 };
2399
2400 delete data;
2401}
2402
2403void Vehicle::sendMavCommandWithLambdaFallback(std::function<void()> lambda, int compId, MAV_CMD command, bool showError, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
2404{
2405
2406 auto* instanceData = firmwarePluginInstanceData();
2407
2408 switch (instanceData->getCommandSupported(command)) {
2410 // command is defintely unsupported, so call the lambda function:
2411 lambda();
2412 break;
2414 // command is definitely supported; just send the command normally:
2416 compId,
2417 command,
2418 showError,
2419 param1,
2420 param2,
2421 param3,
2422 param4,
2423 param5,
2424 param6,
2425 param7
2426 );
2427 break;
2429 // unknown whether the command is supported; send the command
2430 // and let the callback handler call the lambda function if
2431 // the command is not supported:
2433 data->vehicle = this;
2434 data->showError = showError;
2435 data->unsupported_lambda = lambda;
2436
2437 const MavCmdAckHandlerInfo_t handlerInfo {
2438 /* .resultHandler = */ &_sendMavCommandWithLambdaFallbackHandler,
2439 /* .resultHandlerData = */ data,
2440 /* .progressHandler = */ nullptr,
2441 /* .progressHandlerData = */ nullptr
2442 };
2444 &handlerInfo,
2445 compId,
2446 command,
2447 param1,
2448 param2,
2449 param3,
2450 param4,
2451 param5,
2452 param6,
2453 param7
2454 );
2455 break;
2456 }
2457 }
2458}
2459
2460bool Vehicle::isMavCommandPending(int targetCompId, MAV_CMD command)
2461{
2462 bool pending = ((-1) < _findMavCommandListEntryIndex(targetCompId, command));
2463 // qDebug() << "Pending target: " << targetCompId << ", command: " << (int)command << ", pending: " << (pending ? "yes" : "no");
2464 return pending;
2465}
2466
2467int Vehicle::_findMavCommandListEntryIndex(int targetCompId, MAV_CMD command)
2468{
2469 for (int i=0; i<_mavCommandList.count(); i++) {
2470 const MavCommandListEntry_t& entry = _mavCommandList[i];
2471 if (entry.targetCompId == targetCompId && entry.command == command) {
2472 return i;
2473 }
2474 }
2475
2476 return -1;
2477}
2478
2479int Vehicle::_mavCommandResponseCheckTimeoutMSecs()
2480{
2481 // Use shorter check interval during unit tests for faster test execution
2482 return qgcApp()->runningUnitTests() ? 50 : 500;
2483}
2484
2485int Vehicle::_mavCommandAckTimeoutMSecs()
2486{
2487 // Use shorter ack timeout during unit tests for faster test execution
2488 return qgcApp()->runningUnitTests() ? kTestMavCommandAckTimeoutMs : 1200;
2489}
2490
2492{
2493 switch (command) {
2494#ifdef QT_DEBUG
2495 // These MockLink command should be retried so we can create unit tests to test retry code
2501 return true;
2502#endif
2503
2504 // In general we should not retry any commands. This is for safety reasons. For example you don't want an ARM command
2505 // to timeout with no response over a noisy link twice and then suddenly have the third try work 6 seconds later. At that
2506 // point the user could have walked up to the vehicle to see what is going wrong.
2507 //
2508 // We do retry commands which are part of the initial vehicle connect sequence. This makes this process work better over noisy
2509 // links where commands could be lost. Also these commands tend to just be requesting status so if they end up being delayed
2510 // there are no safety concerns that could occur.
2511 case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
2512 case MAV_CMD_REQUEST_MESSAGE:
2513 case MAV_CMD_PREFLIGHT_STORAGE:
2514 case MAV_CMD_RUN_PREARM_CHECKS:
2515 return true;
2516
2517 default:
2518 return false;
2519 }
2520}
2521
2523{
2524 // For some commands we don't care about response as much as we care about sending them regularly.
2525 // This test avoids commands not being sent due to an ACK not being received yet.
2526 // MOTOR_TEST in ardusub is a case where we need a constant stream of commands so it doesn't time out.
2527 switch (command) {
2528 case MAV_CMD_DO_MOTOR_TEST:
2529 return true;
2530 case MAV_CMD_SET_MESSAGE_INTERVAL:
2531 return true;
2532 default:
2533 return false;
2534 }
2535}
2536
2538 bool commandInt,
2539 bool showError,
2540 const MavCmdAckHandlerInfo_t* ackHandlerInfo,
2541 int targetCompId,
2542 MAV_CMD command,
2543 MAV_FRAME frame,
2544 float param1, float param2, float param3, float param4, double param5, double param6, float param7)
2545{
2546 // We can't send commands to compIdAll using this method. The reason being that we would get responses back possibly from multiple components
2547 // which this code can't handle.
2548 // We also can't send the majority of commands again if we are already waiting for a response from that same command. If we did that we would not be able to discern
2549 // which ack was associated with which command.
2550 if ((targetCompId == MAV_COMP_ID_ALL) || (isMavCommandPending(targetCompId, command) && !_commandCanBeDuplicated(command))) {
2551 bool compIdAll = targetCompId == MAV_COMP_ID_ALL;
2552 QString rawCommandName = MissionCommandTree::instance()->rawName(command);
2553
2554 qCDebug(VehicleLog) << QStringLiteral("_sendMavCommandWorker failing %1").arg(compIdAll ? "MAV_COMP_ID_ALL not supported" : "duplicate command") << rawCommandName << param1 << param2 << param3 << param4 << param5 << param6 << param7;
2555
2557 if (ackHandlerInfo && ackHandlerInfo->resultHandler) {
2558 mavlink_command_ack_t ack = {};
2559 ack.result = MAV_RESULT_FAILED;
2560 (*ackHandlerInfo->resultHandler)(ackHandlerInfo->resultHandlerData, targetCompId, ack, failureCode);
2561 } else {
2562 emit mavCommandResult(_systemID, targetCompId, command, MAV_RESULT_FAILED, failureCode);
2563 }
2564 if (showError) {
2565 qgcApp()->showAppMessage(tr("Unable to send command: %1.").arg(compIdAll ? tr("Internal error - MAV_COMP_ID_ALL not supported") : tr("Waiting on previous response to same command.")));
2566 }
2567
2568 return;
2569 }
2570
2571 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
2572 if (!sharedLink) {
2573 qCDebug(VehicleLog) << "_sendMavCommandWorker: primary link gone!";
2574
2576 if (ackHandlerInfo && ackHandlerInfo->resultHandler) {
2577 mavlink_command_ack_t ack = {};
2578 ack.command = command;
2579 ack.result = MAV_RESULT_FAILED;
2580 (*ackHandlerInfo->resultHandler)(ackHandlerInfo->resultHandlerData, targetCompId, ack, failureCode);
2581 } else {
2582 emit mavCommandResult(_systemID, targetCompId, command, MAV_RESULT_FAILED, failureCode);
2583 }
2584
2585 if (showError) {
2586 qgcApp()->showAppMessage(tr("Unable to send command: Vehicle is not connected."));
2587 }
2588 return;
2589 }
2590
2591 MavCommandListEntry_t entry;
2592
2593 entry.useCommandInt = commandInt;
2594 entry.targetCompId = targetCompId;
2595 entry.command = command;
2596 entry.frame = frame;
2597 entry.showError = showError;
2598 entry.ackHandlerInfo = {};
2599 if (ackHandlerInfo) {
2600 entry.ackHandlerInfo = *ackHandlerInfo;
2601 }
2602 entry.rgParam1 = param1;
2603 entry.rgParam2 = param2;
2604 entry.rgParam3 = param3;
2605 entry.rgParam4 = param4;
2606 entry.rgParam5 = param5;
2607 entry.rgParam6 = param6;
2608 entry.rgParam7 = param7;
2609 entry.maxTries = _sendMavCommandShouldRetry(command) ? _mavCommandMaxRetryCount : 1;
2610 entry.ackTimeoutMSecs = sharedLink->linkConfiguration()->isHighLatency() ? _mavCommandAckTimeoutMSecsHighLatency : _mavCommandAckTimeoutMSecs();
2611 entry.elapsedTimer.start();
2612
2613 QString commandStr = _formatMavCommand(command, param1);
2614 qCDebug(VehicleLog) << "Sending" << commandStr << "param1-7:" << command << param1 << param2 << param3 << param4 << param5 << param6 << param7;
2615
2616 _mavCommandList.append(entry);
2617 _sendMavCommandFromList(_mavCommandList.count() - 1);
2618}
2619
2621{
2622 MavCommandListEntry_t commandEntry = _mavCommandList[index];
2623
2624 QString rawCommandName = MissionCommandTree::instance()->rawName(commandEntry.command);
2625 QString friendlyName = MissionCommandTree::instance()->friendlyName(commandEntry.command);
2626
2627 if (++_mavCommandList[index].tryCount > commandEntry.maxTries) {
2628 QString logMsg = QStringLiteral("Giving up sending command after max retries: %1").arg(rawCommandName);
2629
2630 // For REQUEST_MESSAGE commands, also log which message was being requested
2631 if (commandEntry.command == MAV_CMD_REQUEST_MESSAGE) {
2632 int requestedMsgId = static_cast<int>(commandEntry.rgParam1);
2633 const mavlink_message_info_t *info = mavlink_get_message_info_by_id(requestedMsgId);
2634 logMsg += QStringLiteral(" requesting: %1").arg(info->name);
2635 }
2636
2637 qCWarning(VehicleLog) << logMsg;
2638
2639 _mavCommandList.removeAt(index);
2640 if (commandEntry.ackHandlerInfo.resultHandler) {
2641 mavlink_command_ack_t ack = {};
2642 ack.result = MAV_RESULT_FAILED;
2643 (*commandEntry.ackHandlerInfo.resultHandler)(commandEntry.ackHandlerInfo.resultHandlerData, commandEntry.targetCompId, ack, MavCmdResultFailureNoResponseToCommand);
2644 } else {
2645 emit mavCommandResult(_systemID, commandEntry.targetCompId, commandEntry.command, MAV_RESULT_FAILED, MavCmdResultFailureNoResponseToCommand);
2646 }
2647 if (commandEntry.showError) {
2648 qgcApp()->showAppMessage(tr("Vehicle did not respond to command: %1").arg(friendlyName));
2649 }
2650 return;
2651 }
2652
2653 if (commandEntry.tryCount > 1 && !px4Firmware() && commandEntry.command == MAV_CMD_START_RX_PAIR) {
2654 // The implementation of this command comes from the IO layer and is shared across stacks. So for other firmwares
2655 // we aren't really sure whether they are correct or not.
2656 return;
2657 }
2658
2659 QString commandStr = _formatMavCommand(commandEntry.command, commandEntry.rgParam1);
2660 qCDebug(VehicleLog) << "Sending" << commandStr << "tryCount:param1-7" << commandEntry.tryCount << commandEntry.rgParam1 << commandEntry.rgParam2 << commandEntry.rgParam3 << commandEntry.rgParam4 << commandEntry.rgParam5 << commandEntry.rgParam6 << commandEntry.rgParam7;
2661
2662 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
2663 if (!sharedLink) {
2664 qCDebug(VehicleLog) << "_sendMavCommandFromList: primary link gone!";
2665 return;
2666 }
2667
2669
2670 if (commandEntry.useCommandInt) {
2671 mavlink_command_int_t cmd;
2672
2673 memset(&cmd, 0, sizeof(cmd));
2674 cmd.target_system = _systemID;
2675 cmd.target_component = commandEntry.targetCompId;
2676 cmd.command = commandEntry.command;
2677 cmd.frame = commandEntry.frame;
2678 cmd.param1 = commandEntry.rgParam1;
2679 cmd.param2 = commandEntry.rgParam2;
2680 cmd.param3 = commandEntry.rgParam3;
2681 cmd.param4 = commandEntry.rgParam4;
2682 cmd.x = commandEntry.frame == MAV_FRAME_MISSION ? commandEntry.rgParam5 : commandEntry.rgParam5 * 1e7;
2683 cmd.y = commandEntry.frame == MAV_FRAME_MISSION ? commandEntry.rgParam6 : commandEntry.rgParam6 * 1e7;
2684 cmd.z = commandEntry.rgParam7;
2685 mavlink_msg_command_int_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
2687 sharedLink->mavlinkChannel(),
2688 &msg,
2689 &cmd);
2690 } else {
2691 mavlink_command_long_t cmd;
2692
2693 memset(&cmd, 0, sizeof(cmd));
2694 cmd.target_system = _systemID;
2695 cmd.target_component = commandEntry.targetCompId;
2696 cmd.command = commandEntry.command;
2697 cmd.confirmation = 0;
2698 cmd.param1 = commandEntry.rgParam1;
2699 cmd.param2 = commandEntry.rgParam2;
2700 cmd.param3 = commandEntry.rgParam3;
2701 cmd.param4 = commandEntry.rgParam4;
2702 cmd.param5 = static_cast<float>(commandEntry.rgParam5);
2703 cmd.param6 = static_cast<float>(commandEntry.rgParam6);
2704 cmd.param7 = commandEntry.rgParam7;
2705 mavlink_msg_command_long_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
2707 sharedLink->mavlinkChannel(),
2708 &msg,
2709 &cmd);
2710 }
2711
2712 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
2713}
2714
2715void Vehicle::_sendMavCommandResponseTimeoutCheck(void)
2716{
2717 if (_mavCommandList.isEmpty()) {
2718 return;
2719 }
2720
2721 // Walk the list backwards since _sendMavCommandFromList can remove entries
2722 for (int i=_mavCommandList.count()-1; i>=0; i--) {
2723 MavCommandListEntry_t& commandEntry = _mavCommandList[i];
2724 if (commandEntry.elapsedTimer.elapsed() > commandEntry.ackTimeoutMSecs) {
2725 // Try sending command again
2727 }
2728 }
2729}
2730
2731void Vehicle::showCommandAckError(const mavlink_command_ack_t& ack)
2732{
2733 QString rawName = MissionCommandTree::instance()->rawName(static_cast<MAV_CMD>(ack.command));
2734 QString friendlyName = MissionCommandTree::instance()->friendlyName(static_cast<MAV_CMD>(ack.command));
2735 QString commandStr;
2736
2737 if (friendlyName.isEmpty()) {
2738 commandStr = rawName;
2739 } else {
2740 commandStr = QStringLiteral("%1 (%2)").arg(friendlyName).arg(rawName);
2741 }
2742
2743 switch (ack.result) {
2744 case MAV_RESULT_TEMPORARILY_REJECTED:
2745 qgcApp()->showAppMessage(tr("%1 command temporarily rejected").arg(commandStr));
2746 break;
2747 case MAV_RESULT_DENIED:
2748 qgcApp()->showAppMessage(tr("%1 command denied").arg(commandStr));
2749 break;
2750 case MAV_RESULT_UNSUPPORTED:
2751 qgcApp()->showAppMessage(tr("%1 command not supported").arg(commandStr));
2752 break;
2753 case MAV_RESULT_FAILED:
2754 qgcApp()->showAppMessage(tr("%1 command failed").arg(commandStr));
2755 break;
2756 default:
2757 // Do nothing
2758 break;
2759 }
2760}
2761
2762void Vehicle::_handleCommandAck(mavlink_message_t& message)
2763{
2764 mavlink_command_ack_t ack;
2765 mavlink_msg_command_ack_decode(&message, &ack);
2766
2767 QString rawCommandName = MissionCommandTree::instance()->rawName(static_cast<MAV_CMD>(ack.command));
2768 QString logMsg = QStringLiteral("_handleCommandAck command(%1) result(%2)").arg(rawCommandName).arg(QGCMAVLink::mavResultToString(static_cast<MAV_RESULT>(ack.result)));
2769
2770 // For REQUEST_MESSAGE commands, also log which message was requested
2771 if (ack.command == MAV_CMD_REQUEST_MESSAGE) {
2772 int entryIndex = _findMavCommandListEntryIndex(message.compid, static_cast<MAV_CMD>(ack.command));
2773 if (entryIndex != -1) {
2774 int requestedMsgId = static_cast<int>(_mavCommandList[entryIndex].rgParam1);
2775 const mavlink_message_info_t *info = mavlink_get_message_info_by_id(requestedMsgId);
2776 QString msgName = info ? QString(info->name) : QString::number(requestedMsgId);
2777 logMsg += QStringLiteral(" msgId(%1)").arg(msgName);
2778 }
2779 }
2780
2781 qCDebug(VehicleLog) << logMsg;
2782
2783 if (ack.command == MAV_CMD_DO_SET_ROI_LOCATION) {
2784 if (ack.result == MAV_RESULT_ACCEPTED) {
2785 _isROIEnabled = true;
2786 emit isROIEnabledChanged();
2787 }
2788 }
2789
2790 if (ack.command == MAV_CMD_DO_SET_ROI_NONE) {
2791 if (ack.result == MAV_RESULT_ACCEPTED) {
2792 _isROIEnabled = false;
2793 emit isROIEnabledChanged();
2794 }
2795 }
2796
2797 if (ack.command == MAV_CMD_PREFLIGHT_STORAGE) {
2798 auto result = (ack.result == MAV_RESULT_ACCEPTED);
2799 emit sensorsParametersResetAck(result);
2800 }
2801
2802#if !defined(QGC_NO_ARDUPILOT_DIALECT)
2803 if (ack.command == MAV_CMD_FLASH_BOOTLOADER && ack.result == MAV_RESULT_ACCEPTED) {
2804 qgcApp()->showAppMessage(tr("Bootloader flash succeeded"));
2805 }
2806#endif
2807
2808 int entryIndex = _findMavCommandListEntryIndex(message.compid, static_cast<MAV_CMD>(ack.command));
2809 if (entryIndex != -1) {
2810 if (ack.result == MAV_RESULT_IN_PROGRESS) {
2811 MavCommandListEntry_t commandEntry;
2812 if (px4Firmware() && ack.command == MAV_CMD_DO_AUTOTUNE_ENABLE) {
2813 // HacK to support PX4 autotune which does not send final result ack and just sends in progress
2814 commandEntry = _mavCommandList.takeAt(entryIndex);
2815 } else {
2816 // Command has not completed yet, don't remove
2817 MavCommandListEntry_t& commandEntryRef = _mavCommandList[entryIndex];
2818 commandEntryRef.maxTries = 1; // Vehicle responsed to command so don't retry
2819 commandEntryRef.elapsedTimer.start(); // We've heard from vehicle, restart elapsed timer for no ack received timeout
2820 commandEntry = commandEntryRef;
2821 }
2822
2823 if (commandEntry.ackHandlerInfo.progressHandler) {
2824 (*commandEntry.ackHandlerInfo.progressHandler)(commandEntry.ackHandlerInfo.progressHandlerData, message.compid, ack);
2825 }
2826 } else {
2827 MavCommandListEntry_t commandEntry = _mavCommandList.takeAt(entryIndex);
2828
2829 if (commandEntry.ackHandlerInfo.resultHandler) {
2830 (*commandEntry.ackHandlerInfo.resultHandler)(commandEntry.ackHandlerInfo.resultHandlerData, message.compid, ack, MavCmdResultCommandResultOnly);
2831 } else {
2832 if (commandEntry.showError) {
2834 }
2835 emit mavCommandResult(_systemID, message.compid, ack.command, ack.result, MavCmdResultCommandResultOnly);
2836 }
2837 }
2838 } else {
2839 qCDebug(VehicleLog) << "_handleCommandAck Ack not in list" << rawCommandName;
2840 }
2841
2842 // advance PID tuning setup/teardown
2843 if (ack.command == MAV_CMD_SET_MESSAGE_INTERVAL) {
2844 _mavlinkStreamConfig.gotSetMessageIntervalAck();
2845 }
2846}
2847
2848void Vehicle::_removeRequestMessageInfo(int compId, int msgId)
2849{
2850 if (_requestMessageInfoMap.contains(compId) && _requestMessageInfoMap[compId].contains(msgId)) {
2851 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(msgId);
2852 const QString msgName = info ? QString(info->name) : QString::number(msgId);
2853
2854 delete _requestMessageInfoMap[compId][msgId];
2855 _requestMessageInfoMap[compId].remove(msgId);
2856 qCDebug(VehicleLog)
2857 << "removed active request compId:msgId"
2858 << compId
2859 << msgName
2860 << "remainingActive"
2861 << _requestMessageInfoMap[compId].count();
2862 if (_requestMessageInfoMap[compId].isEmpty()) {
2863 _requestMessageInfoMap.remove(compId);
2864 }
2865 _requestMessageSendNextFromQueue(compId);
2866 } else {
2867 qWarning() << "compId:msgId not found" << compId << msgId;
2868 }
2869}
2870
2871bool Vehicle::_requestMessageDuplicate(int compId, int msgId) const
2872{
2873 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(msgId);
2874 const QString msgName = info ? QString(info->name) : QString::number(msgId);
2875
2876 if (_requestMessageInfoMap.contains(compId) && _requestMessageInfoMap[compId].contains(msgId)) {
2877 qCDebug(VehicleLog) << "duplicate in active map - compId:msgId" << compId << msgName;
2878 return true;
2879 }
2880
2881 if (_requestMessageQueueMap.contains(compId)) {
2882 for (const RequestMessageInfo_t* requestMessageInfo : _requestMessageQueueMap[compId]) {
2883 if (requestMessageInfo->msgId == msgId) {
2884 qCDebug(VehicleLog) << "duplicate in queue - compId:msgId" << compId << msgName;
2885 return true;
2886 }
2887 }
2888 }
2889
2890 return false;
2891}
2892
2893void Vehicle::_requestMessageSendNow(RequestMessageInfo_t* requestMessageInfo)
2894{
2895 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(requestMessageInfo->msgId);
2896 const QString msgName = info ? QString(info->name) : QString::number(requestMessageInfo->msgId);
2897
2898 _requestMessageInfoMap[requestMessageInfo->compId][requestMessageInfo->msgId] = requestMessageInfo;
2899
2900 const int queueDepth = _requestMessageQueueMap.contains(requestMessageInfo->compId)
2901 ? _requestMessageQueueMap[requestMessageInfo->compId].count()
2902 : 0;
2903 qCDebug(VehicleLog)
2904 << "sending now compId:msgId"
2905 << requestMessageInfo->compId
2906 << msgName
2907 << "queueDepth"
2908 << queueDepth;
2909
2911 handlerInfo.resultHandler = _requestMessageCmdResultHandler;
2912 handlerInfo.resultHandlerData = requestMessageInfo;
2913
2914 _sendMavCommandWorker(false, // commandInt
2915 false, // showError
2916 &handlerInfo,
2917 requestMessageInfo->compId,
2918 MAV_CMD_REQUEST_MESSAGE,
2919 MAV_FRAME_GLOBAL,
2920 requestMessageInfo->msgId,
2921 requestMessageInfo->param1,
2922 requestMessageInfo->param2,
2923 requestMessageInfo->param3,
2924 requestMessageInfo->param4,
2925 requestMessageInfo->param5,
2926 0);
2927}
2928
2929void Vehicle::_requestMessageSendNextFromQueue(int compId)
2930{
2931 if (_requestMessageInfoMap.contains(compId) && !_requestMessageInfoMap[compId].isEmpty()) {
2932 qCDebug(VehicleLog)
2933 << "active request still in progress for compId"
2934 << compId
2935 << "activeCount"
2936 << _requestMessageInfoMap[compId].count();
2937 return;
2938 }
2939
2940 if (!_requestMessageQueueMap.contains(compId) || _requestMessageQueueMap[compId].isEmpty()) {
2941 qCDebug(VehicleLog) << "no queued request for compId" << compId;
2942 _requestMessageQueueMap.remove(compId);
2943 return;
2944 }
2945
2946 RequestMessageInfo_t* requestMessageInfo = _requestMessageQueueMap[compId].takeFirst();
2947 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(requestMessageInfo->msgId);
2948 const QString msgName = info ? QString(info->name) : QString::number(requestMessageInfo->msgId);
2949 const int remainingQueue = _requestMessageQueueMap[compId].count();
2950 qCDebug(VehicleLog)
2951 << "dequeued next request compId:msgId"
2952 << compId
2953 << msgName
2954 << "remainingQueue"
2955 << remainingQueue;
2956
2957 if (_requestMessageQueueMap[compId].isEmpty()) {
2958 _requestMessageQueueMap.remove(compId);
2959 }
2960
2961 _requestMessageSendNow(requestMessageInfo);
2962}
2963
2964
2965void Vehicle::_waitForMavlinkMessageMessageReceivedHandler(const mavlink_message_t& message)
2966{
2967 if (_requestMessageInfoMap.contains(message.compid) && _requestMessageInfoMap[message.compid].contains(message.msgid)) {
2968 auto pInfo = _requestMessageInfoMap[message.compid][message.msgid];
2969 auto resultHandler = pInfo->resultHandler;
2970 auto resultHandlerData = pInfo->resultHandlerData;
2971
2972 pInfo->messageReceived = true;
2973 pInfo->message = message;
2974
2975 const mavlink_message_info_t *info = mavlink_get_message_info_by_id(message.msgid);
2976 QString msgName = info ? QString(info->name) : QString::number(message.msgid);
2977 const int activeCount = _requestMessageInfoMap.contains(message.compid) ? _requestMessageInfoMap[message.compid].count() : 0;
2978 const int queueDepth = _requestMessageQueueMap.contains(message.compid) ? _requestMessageQueueMap[message.compid].count() : 0;
2979 qCDebug(VehicleLog)
2980 << "message received - compId:msgId"
2981 << message.compid
2982 << msgName
2983 << "activeCount"
2984 << activeCount
2985 << "queueDepth"
2986 << queueDepth;
2987
2988 if (pInfo->commandAckReceived) {
2989 _removeRequestMessageInfo(message.compid, message.msgid);
2990 (*resultHandler)(resultHandlerData, MAV_RESULT_ACCEPTED, RequestMessageNoFailure, message);
2991 }
2992 } else {
2993 // We use any incoming message as a trigger to check timeouts on message requests
2994
2995 for (auto& compIdEntry : _requestMessageInfoMap) {
2996 for (auto requestMessageInfo : compIdEntry) {
2997 // Unit-test environments can have enough scheduling jitter that a 50ms
2998 // response deadline causes false request-message timeouts.
2999 const int messageWaitTimeoutMs = qgcApp()->runningUnitTests() ? 500 : 1000;
3000 if (requestMessageInfo->messageWaitElapsedTimer.isValid() && requestMessageInfo->messageWaitElapsedTimer.elapsed() > messageWaitTimeoutMs) {
3001 auto resultHandler = requestMessageInfo->resultHandler;
3002 auto resultHandlerData = requestMessageInfo->resultHandlerData;
3003
3004 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(requestMessageInfo->msgId);
3005 QString msgName = info ? info->name : QString::number(requestMessageInfo->msgId);
3006 const int queueDepth = _requestMessageQueueMap.contains(requestMessageInfo->compId) ? _requestMessageQueueMap[requestMessageInfo->compId].count() : 0;
3007 qCDebug(VehicleLog)
3008 << "request message timed out - compId:msgId"
3009 << requestMessageInfo->compId
3010 << msgName
3011 << "queueDepth"
3012 << queueDepth;
3013
3014 _removeRequestMessageInfo(requestMessageInfo->compId, requestMessageInfo->msgId);
3015
3016 mavlink_message_t timeoutMessage;
3017 (*resultHandler)(resultHandlerData, MAV_RESULT_FAILED, RequestMessageFailureMessageNotReceived, timeoutMessage);
3018
3019 return; // We only handle one timeout at a time
3020 }
3021 }
3022 }
3023 }
3024}
3025
3026void Vehicle::_requestMessageCmdResultHandler(void* resultHandlerData_, [[maybe_unused]] int compId, const mavlink_command_ack_t& ack, MavCmdResultFailureCode_t failureCode)
3027{
3028 auto requestMessageInfo = static_cast<RequestMessageInfo_t*>(resultHandlerData_);
3029 auto resultHandler = requestMessageInfo->resultHandler;
3030 auto resultHandlerData = requestMessageInfo->resultHandlerData;
3031 Vehicle* vehicle = requestMessageInfo->vehicle; // QPointer converts to raw pointer, null if Vehicle destroyed
3032
3033 // Vehicle was destroyed before callback fired - clean up and return without accessing vehicle
3034 if (!vehicle) {
3035 qCDebug(VehicleLog) << "Vehicle destroyed before callback - skipping";
3036 delete requestMessageInfo;
3037 return;
3038 }
3039
3040 requestMessageInfo->commandAckReceived = true;
3041 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(requestMessageInfo->msgId);
3042 const QString msgName = info ? QString(info->name) : QString::number(requestMessageInfo->msgId);
3043 qCDebug(VehicleLog)
3044 << "ack for requestMessage compId:msgId"
3045 << requestMessageInfo->compId
3046 << msgName
3047 << "ack"
3048 << QGCMAVLink::mavResultToString(static_cast<MAV_RESULT>(ack.result))
3049 << "failureCode"
3050 << mavCmdResultFailureCodeToString(failureCode);
3051
3052 if (ack.result != MAV_RESULT_ACCEPTED) {
3053 mavlink_message_t ackMessage;
3055
3056 switch (failureCode) {
3058 requestMessageFailureCode = RequestMessageFailureCommandError;
3059 break;
3061 requestMessageFailureCode = RequestMessageFailureCommandNotAcked;
3062 break;
3064 requestMessageFailureCode = RequestMessageFailureDuplicate;
3065 break;
3066 }
3067
3068 vehicle->_removeRequestMessageInfo(requestMessageInfo->compId, requestMessageInfo->msgId);
3069
3070 (*resultHandler)(resultHandlerData, static_cast<MAV_RESULT>(ack.result), requestMessageFailureCode, ackMessage);
3071
3072 return;
3073 }
3074
3075 if (requestMessageInfo->messageReceived) {
3076 auto message = requestMessageInfo->message;
3077 vehicle->_removeRequestMessageInfo(requestMessageInfo->compId, requestMessageInfo->msgId);
3078 (*resultHandler)(resultHandlerData, MAV_RESULT_ACCEPTED, RequestMessageNoFailure, message);
3079 return;
3080 }
3081
3082 // We have the ack, but we are still waiting for the message. Start the timer to wait for the message
3083 requestMessageInfo->messageWaitElapsedTimer.start();
3084}
3085
3086void Vehicle::_requestMessageWaitForMessageResultHandler(void* resultHandlerData, bool noResponsefromVehicle, const mavlink_message_t& message)
3087{
3088 RequestMessageInfo_t* pInfo = static_cast<RequestMessageInfo_t*>(resultHandlerData);
3089
3090 pInfo->messageReceived = true;
3091 if (pInfo->commandAckReceived) {
3092 (*pInfo->resultHandler)(pInfo->resultHandlerData, noResponsefromVehicle ? MAV_RESULT_FAILED : MAV_RESULT_ACCEPTED, noResponsefromVehicle ? RequestMessageFailureMessageNotReceived : RequestMessageNoFailure, message);
3093 } else {
3094 // Result handler will be called when we get the Ack
3095 pInfo->message = message;
3096 }
3097}
3098
3099void Vehicle::requestMessage(RequestMessageResultHandler resultHandler, void* resultHandlerData, int compId, int messageId, float param1, float param2, float param3, float param4, float param5)
3100{
3101 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(messageId);
3102 const QString msgName = info ? QString(info->name) : QString::number(messageId);
3103 const int activeCount = _requestMessageInfoMap.contains(compId) ? _requestMessageInfoMap[compId].count() : 0;
3104 const int queueDepth = _requestMessageQueueMap.contains(compId) ? _requestMessageQueueMap[compId].count() : 0;
3105 qCDebug(VehicleLog)
3106 << "incoming request compId:msgId"
3107 << compId
3108 << msgName
3109 << "activeCount"
3110 << activeCount
3111 << "queueDepth"
3112 << queueDepth;
3113
3114 auto requestMessageInfo = new RequestMessageInfo_t;
3115 requestMessageInfo->vehicle = this;
3116 requestMessageInfo->compId = compId;
3117 requestMessageInfo->msgId = messageId;
3118 requestMessageInfo->param1 = param1;
3119 requestMessageInfo->param2 = param2;
3120 requestMessageInfo->param3 = param3;
3121 requestMessageInfo->param4 = param4;
3122 requestMessageInfo->param5 = param5;
3123 requestMessageInfo->resultHandler = resultHandler;
3124 requestMessageInfo->resultHandlerData = resultHandlerData;
3125
3126 if (_requestMessageDuplicate(compId, messageId)) {
3127 mavlink_message_t ackMessage = {};
3128 qCWarning(VehicleLog) << "failing exact duplicate compId:msgId" << compId << msgName;
3129 (*resultHandler)(resultHandlerData,
3130 MAV_RESULT_FAILED,
3132 ackMessage);
3133 delete requestMessageInfo;
3134 return;
3135 }
3136
3137 if (_requestMessageInfoMap.contains(compId) && !_requestMessageInfoMap[compId].isEmpty()) {
3138 _requestMessageQueueMap[compId].append(requestMessageInfo);
3139 qCDebug(VehicleLog)
3140 << "queued request compId:msgId"
3141 << compId
3142 << msgName
3143 << "newQueueDepth"
3144 << _requestMessageQueueMap[compId].count();
3145 return;
3146 }
3147
3148 _requestMessageSendNow(requestMessageInfo);
3149}
3150
3151void Vehicle::setPrearmError(const QString& prearmError)
3152{
3153 _prearmError = prearmError;
3154 emit prearmErrorChanged(_prearmError);
3155 if (!_prearmError.isEmpty()) {
3156 _prearmErrorTimer.start();
3157 }
3158}
3159
3160void Vehicle::_prearmErrorTimeout()
3161{
3162 setPrearmError(QString());
3163}
3164
3165void Vehicle::setFirmwareVersion(int majorVersion, int minorVersion, int patchVersion, FIRMWARE_VERSION_TYPE versionType)
3166{
3167 _firmwareMajorVersion = majorVersion;
3168 _firmwareMinorVersion = minorVersion;
3169 _firmwarePatchVersion = patchVersion;
3170 _firmwareVersionType = versionType;
3172}
3173
3174void Vehicle::setFirmwareCustomVersion(int majorVersion, int minorVersion, int patchVersion)
3175{
3176 _firmwareCustomMajorVersion = majorVersion;
3177 _firmwareCustomMinorVersion = minorVersion;
3178 _firmwareCustomPatchVersion = patchVersion;
3180}
3181
3183{
3184 return QGCMAVLink::firmwareVersionTypeToString(_firmwareVersionType);
3185}
3186
3187void Vehicle::_rebootCommandResultHandler(void* resultHandlerData, int /*compId*/, const mavlink_command_ack_t& ack, MavCmdResultFailureCode_t failureCode)
3188{
3189 Vehicle* vehicle = static_cast<Vehicle*>(resultHandlerData);
3190
3191 if (ack.result != MAV_RESULT_ACCEPTED) {
3192 switch (failureCode) {
3194 qCDebug(VehicleLog) << QStringLiteral("MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN error(%1)").arg(ack.result);
3195 break;
3197 qCDebug(VehicleLog) << "MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN failed: no response from vehicle";
3198 break;
3200 qCDebug(VehicleLog) << "MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN failed: duplicate command";
3201 break;
3202 }
3203 qgcApp()->showAppMessage(tr("Vehicle reboot failed."));
3204 } else {
3205 vehicle->closeVehicle();
3206 }
3207}
3208
3210{
3211 Vehicle::MavCmdAckHandlerInfo_t handlerInfo = {};
3212 handlerInfo.resultHandler = _rebootCommandResultHandler;
3213 handlerInfo.resultHandlerData = this;
3214
3215 sendMavCommandWithHandler(&handlerInfo, _defaultComponentId, MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, 1);
3216}
3217
3219{
3220 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
3221 if (!sharedLink) {
3222 qCDebug(VehicleLog) << "startCalibration: primary link gone!";
3223 return;
3224 }
3225
3226 float param1 = 0;
3227 float param2 = 0;
3228 float param3 = 0;
3229 float param4 = 0;
3230 float param5 = 0;
3231 float param6 = 0;
3232 float param7 = 0;
3233
3234 switch (calType) {
3236 param1 = 1;
3237 break;
3239 param2 = 1;
3240 break;
3242 param4 = 1;
3243 break;
3245 param4 = 2;
3246 break;
3248 param5 = 1;
3249 break;
3251 param5 = 2;
3252 break;
3254 param7 = 1;
3255 break;
3257 param6 = 1;
3258 break;
3260 param3 = 1;
3261 break;
3263 param6 = 1;
3264 break;
3266 param3 = 1;
3267 break;
3269 param3 = 1; // GroundPressure/Airspeed
3270 if (multiRotor() || rover()) {
3271 // Gyro cal for ArduCopter only
3272 param1 = 1;
3273 }
3274 break;
3276 param5 = 4;
3277 break;
3279 default:
3280 break;
3281 }
3282
3283 // We can't use sendMavCommand here since we have no idea how long it will be before the command returns a result. This in turn
3284 // causes the retry logic to break down.
3286 mavlink_msg_command_long_pack_chan(MAVLinkProtocol::instance()->getSystemId(),
3288 sharedLink->mavlinkChannel(),
3289 &msg,
3290 id(),
3291 defaultComponentId(), // target component
3292 MAV_CMD_PREFLIGHT_CALIBRATION, // command id
3293 0, // 0=first transmission of command
3294 param1, param2, param3, param4, param5, param6, param7);
3295 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
3296}
3297
3298void Vehicle::stopCalibration(bool showError)
3299{
3300 sendMavCommand(defaultComponentId(), // target component
3301 MAV_CMD_PREFLIGHT_CALIBRATION, // command id
3302 showError,
3303 0, // gyro cal
3304 0, // mag cal
3305 0, // ground pressure
3306 0, // radio cal
3307 0, // accel cal
3308 0, // airspeed cal
3309 0); // unused
3310}
3311
3313{
3314 sendMavCommand(defaultComponentId(), // target component
3315 MAV_CMD_PREFLIGHT_UAVCAN, // command id
3316 true, // showError
3317 1); // start config
3318}
3319
3321{
3322 sendMavCommand(defaultComponentId(), // target component
3323 MAV_CMD_PREFLIGHT_UAVCAN, // command id
3324 true, // showError
3325 0); // stop config
3326}
3327
3328void Vehicle::setSoloFirmware(bool soloFirmware)
3329{
3330 if (soloFirmware != _soloFirmware) {
3331 _soloFirmware = soloFirmware;
3333 }
3334}
3335
3336void Vehicle::motorTest(int motor, int percent, int timeoutSecs, bool showError)
3337{
3338 sendMavCommand(_defaultComponentId, MAV_CMD_DO_MOTOR_TEST, showError, motor, MOTOR_TEST_THROTTLE_PERCENT, percent, timeoutSecs, 0, MOTOR_TEST_ORDER_BOARD);
3339}
3340
3342{
3343 return _firmwarePlugin->brandImageIndoor(this);
3344}
3345
3347{
3348 return _firmwarePlugin->brandImageOutdoor(this);
3349}
3350
3352{
3353 if (_offlineEditingVehicle) {
3354 _defaultComponentId = defaultComponentId;
3355 } else {
3356 qCWarning(VehicleLog) << "Call to Vehicle::setOfflineEditingDefaultComponentId on vehicle which is not offline";
3357 }
3358}
3359
3360void Vehicle::setVtolInFwdFlight(bool vtolInFwdFlight)
3361{
3362 if (_vtolInFwdFlight != vtolInFwdFlight) {
3363 sendMavCommand(_defaultComponentId,
3364 MAV_CMD_DO_VTOL_TRANSITION,
3365 true, // show errors
3366 vtolInFwdFlight ? MAV_VTOL_STATE_FW : MAV_VTOL_STATE_MC, // transition state
3367 0, 0, 0, 0, 0, 0); // param 2-7 unused
3368 }
3369}
3370
3372{
3373 sendMavCommand(_defaultComponentId, MAV_CMD_LOGGING_START, false /* showError */);
3374}
3375
3377{
3378 sendMavCommand(_defaultComponentId, MAV_CMD_LOGGING_STOP, false /* showError */);
3379}
3380
3381void Vehicle::_ackMavlinkLogData(uint16_t sequence)
3382{
3383 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
3384 if (!sharedLink) {
3385 qCDebug(VehicleLog) << "_ackMavlinkLogData: primary link gone!";
3386 return;
3387 }
3388
3390 mavlink_logging_ack_t ack;
3391
3392 memset(&ack, 0, sizeof(ack));
3393 ack.sequence = sequence;
3394 ack.target_component = _defaultComponentId;
3395 ack.target_system = id();
3396 mavlink_msg_logging_ack_encode_chan(
3397 MAVLinkProtocol::instance()->getSystemId(),
3399 sharedLink->mavlinkChannel(),
3400 &msg,
3401 &ack);
3402 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
3403}
3404
3405void Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message)
3406{
3407 mavlink_logging_data_t log;
3408 mavlink_msg_logging_data_decode(&message, &log);
3409 if (static_cast<size_t>(log.length) > sizeof(log.data)) {
3410 qWarning() << "Invalid length for LOGGING_DATA, discarding." << log.length;
3411 } else {
3412 emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence,
3413 log.first_message_offset, QByteArray((const char*)log.data, log.length), false);
3414 }
3415}
3416
3417void Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message)
3418{
3419 mavlink_logging_data_acked_t log;
3420 mavlink_msg_logging_data_acked_decode(&message, &log);
3421 _ackMavlinkLogData(log.sequence);
3422 if (static_cast<size_t>(log.length) > sizeof(log.data)) {
3423 qWarning() << "Invalid length for LOGGING_DATA_ACKED, discarding." << log.length;
3424 } else {
3425 emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence,
3426 log.first_message_offset, QByteArray((const char*)log.data, log.length), false);
3427 }
3428}
3429
3431{
3432 firmwarePluginInstanceData->setParent(this);
3433 _firmwarePluginInstanceData = firmwarePluginInstanceData;
3434}
3435
3437{
3438 return _firmwarePlugin->missionFlightMode();
3439}
3440
3442{
3443 return _firmwarePlugin->pauseFlightMode();
3444}
3445
3447{
3448 return _firmwarePlugin->rtlFlightMode();
3449}
3450
3452{
3453 return _firmwarePlugin->smartRTLFlightMode();
3454}
3455
3457{
3458 return _firmwarePlugin->landFlightMode();
3459}
3460
3462{
3463 return _firmwarePlugin->takeControlFlightMode();
3464}
3465
3467{
3468 return _firmwarePlugin->followFlightMode();
3469}
3470
3472{
3473 return _firmwarePlugin->motorDetectionFlightMode();
3474}
3475
3477{
3478 return _firmwarePlugin->stabilizedFlightMode();
3479}
3480
3482{
3483 if(_firmwarePlugin)
3484 return _firmwarePlugin->vehicleImageOpaque(this);
3485 else
3486 return QString();
3487}
3488
3490{
3491 if(_firmwarePlugin)
3492 return _firmwarePlugin->vehicleImageOutline(this);
3493 else
3494 return QString();
3495}
3496
3497const QVariantList& Vehicle::toolIndicators()
3498{
3499 if(_firmwarePlugin) {
3500 return _firmwarePlugin->toolIndicators(this);
3501 }
3502 static QVariantList emptyList;
3503 return emptyList;
3504}
3505
3506void Vehicle::_setupAutoDisarmSignalling()
3507{
3508 QString param = _firmwarePlugin->autoDisarmParameter(this);
3509
3510 if (!param.isEmpty() && _parameterManager->parameterExists(ParameterManager::defaultComponentId, param)) {
3511 Fact* fact = _parameterManager->getParameter(ParameterManager::defaultComponentId,param);
3512 connect(fact, &Fact::rawValueChanged, this, &Vehicle::autoDisarmChanged);
3513 emit autoDisarmChanged();
3514 }
3515}
3516
3518{
3519 QString param = _firmwarePlugin->autoDisarmParameter(this);
3520
3521 if (!param.isEmpty() && _parameterManager->parameterExists(ParameterManager::defaultComponentId, param)) {
3522 Fact* fact = _parameterManager->getParameter(ParameterManager::defaultComponentId,param);
3523 return fact->rawValue().toDouble() > 0;
3524 }
3525
3526 return false;
3527}
3528
3529void Vehicle::_updateDistanceHeadingHome()
3530{
3531 if (coordinate().isValid() && homePosition().isValid()) {
3532 _distanceToHomeFact.setRawValue(coordinate().distanceTo(homePosition()));
3533 if (_distanceToHomeFact.rawValue().toDouble() > 1.0) {
3534 _headingToHomeFact.setRawValue(coordinate().azimuthTo(homePosition()));
3535 _headingFromHomeFact.setRawValue(homePosition().azimuthTo(coordinate()));
3536 } else {
3537 _headingToHomeFact.setRawValue(qQNaN());
3538 _headingFromHomeFact.setRawValue(qQNaN());
3539 }
3540 } else {
3541 _distanceToHomeFact.setRawValue(qQNaN());
3542 _headingToHomeFact.setRawValue(qQNaN());
3543 _headingFromHomeFact.setRawValue(qQNaN());
3544 }
3545}
3546
3547void Vehicle::_updateHeadingToNextWP()
3548{
3549 const int currentIndex = _missionManager->currentIndex();
3550 QList<MissionItem*> llist = _missionManager->missionItems();
3551
3552 if(llist.size()>currentIndex && currentIndex!=-1
3553 && llist[currentIndex]->coordinate().longitude()!=0.0
3554 && coordinate().distanceTo(llist[currentIndex]->coordinate())>5.0 ){
3555
3556 _headingToNextWPFact.setRawValue(coordinate().azimuthTo(llist[currentIndex]->coordinate()));
3557 }
3558 else{
3559 _headingToNextWPFact.setRawValue(qQNaN());
3560 }
3561}
3562
3563void Vehicle::_updateMissionItemIndex()
3564{
3565 const int currentIndex = _missionManager->currentIndex();
3566
3567 unsigned offset = 0;
3568 if (!_firmwarePlugin->sendHomePositionToVehicle()) {
3569 offset = 1;
3570 }
3571
3572 _missionItemIndexFact.setRawValue(currentIndex + offset);
3573}
3574
3575void Vehicle::_updateDistanceHeadingGCS()
3576{
3577 QGeoCoordinate gcsPosition = QGCPositionManager::instance()->gcsPosition();
3578 if (coordinate().isValid() && gcsPosition.isValid()) {
3579 _distanceToGCSFact.setRawValue(coordinate().distanceTo(gcsPosition));
3580 _headingFromGCSFact.setRawValue(gcsPosition.azimuthTo(coordinate()));
3581 } else {
3582 _distanceToGCSFact.setRawValue(qQNaN());
3583 _headingFromGCSFact.setRawValue(qQNaN());
3584 }
3585}
3586
3587void Vehicle::_updateHomepoint()
3588{
3589 const bool setHomeCmdSupported = firmwarePlugin()->supportedMissionCommands(vehicleClass()).contains(MAV_CMD_DO_SET_HOME);
3590 const bool updateHomeActivated = SettingsManager::instance()->flyViewSettings()->updateHomePosition()->rawValue().toBool();
3591 if(setHomeCmdSupported && updateHomeActivated){
3592 QGeoCoordinate gcsPosition = QGCPositionManager::instance()->gcsPosition();
3593 if (coordinate().isValid() && gcsPosition.isValid()) {
3595 MAV_CMD_DO_SET_HOME, false,
3596 0,
3597 0, 0, 0,
3598 static_cast<float>(gcsPosition.latitude()) ,
3599 static_cast<float>(gcsPosition.longitude()),
3600 static_cast<float>(gcsPosition.altitude()));
3601 }
3602 }
3603}
3604
3605void Vehicle::_updateHobbsMeter()
3606{
3607 _hobbsFact.setRawValue(hobbsMeter());
3608}
3609
3611{
3612 _initialPlanRequestComplete = true;
3614}
3615
3616void Vehicle::sendPlan(QString planFile)
3617{
3619}
3620
3622{
3623 return _firmwarePlugin->getHobbsMeter(this);
3624}
3625
3626void Vehicle::_vehicleParamLoaded(bool ready)
3627{
3628 //-- TODO: This seems silly but can you think of a better
3629 // way to update this?
3630 if(ready) {
3631 emit hobbsMeterChanged();
3632 }
3633}
3634
3635void Vehicle::_mavlinkMessageStatus(int uasId, uint64_t totalSent, uint64_t totalReceived, uint64_t totalLoss, float lossPercent)
3636{
3637 if(uasId == _systemID) {
3638 _mavlinkSentCount = totalSent;
3639 _mavlinkReceivedCount = totalReceived;
3640 _mavlinkLossCount = totalLoss;
3641 _mavlinkLossPercent = lossPercent;
3642 emit mavlinkStatusChanged();
3643 }
3644}
3645
3646int Vehicle::versionCompare(const QString& compare) const
3647{
3648 return _firmwarePlugin->versionCompare(this, compare);
3649}
3650
3651int Vehicle::versionCompare(int major, int minor, int patch) const
3652{
3653 return _firmwarePlugin->versionCompare(this, major, minor, patch);
3654}
3655
3657{
3658 bool liveUpdate = mode != ModeDisabled;
3659 setLiveUpdates(liveUpdate);
3660 _setpointFactGroup.setLiveUpdates(liveUpdate);
3661 _localPositionFactGroup.setLiveUpdates(liveUpdate);
3662 _localPositionSetpointFactGroup.setLiveUpdates(liveUpdate);
3663
3664 switch (mode) {
3665 case ModeDisabled:
3666 _mavlinkStreamConfig.restoreDefaults();
3667 break;
3669 _mavlinkStreamConfig.setHighRateRateAndAttitude();
3670 break;
3672 _mavlinkStreamConfig.setHighRateVelAndPos();
3673 break;
3675 _mavlinkStreamConfig.setHighRateAltAirspeed();
3676 // reset the altitude offset to the current value, so the plotted value is around 0
3677 if (!qIsNaN(_altitudeTuningOffset)) {
3678 _altitudeTuningOffset += _altitudeTuningFact.rawValue().toDouble();
3679 _altitudeTuningSetpointFact.setRawValue(0.f);
3680 _altitudeTuningFact.setRawValue(0.f);
3681 }
3682 break;
3683 }
3684}
3685
3686void Vehicle::_setMessageInterval(int messageId, int rate)
3687{
3689 MAV_CMD_SET_MESSAGE_INTERVAL,
3690 true, // show error
3691 messageId,
3692 rate);
3693}
3694
3695QString Vehicle::_formatMavCommand(MAV_CMD command, float param1)
3696{
3697 QString commandName = MissionCommandTree::instance()->rawName(command);
3698
3699 if (command == MAV_CMD_REQUEST_MESSAGE && param1 > 0) {
3700 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(static_cast<uint32_t>(param1));
3701 QString param1Str = info ? QString("%1(%2)").arg(param1).arg(info->name) : QString::number(param1);
3702 return QString("%1: %2").arg(commandName).arg(param1Str);
3703 }
3704 return QString("%1: %2").arg(commandName).arg(param1);
3705}
3706
3708{
3709 return !_initialConnectStateMachine->active();
3710}
3711
3712void Vehicle::_initializeCsv()
3713{
3714 if (!SettingsManager::instance()->mavlinkSettings()->saveCsvTelemetry()->rawValue().toBool()) {
3715 return;
3716 }
3717 QString now = QDateTime::currentDateTime().toString("yyyy-MM-dd hh-mm-ss");
3718 QString fileName = QString("%1 vehicle%2.csv").arg(now).arg(_systemID);
3719 QDir saveDir(SettingsManager::instance()->appSettings()->telemetrySavePath());
3720 _csvLogFile.setFileName(saveDir.absoluteFilePath(fileName));
3721
3722 if (!_csvLogFile.open(QIODevice::Append)) {
3723 qCWarning(VehicleLog) << "unable to open file for csv logging, Stopping csv logging!";
3724 return;
3725 }
3726
3727 QTextStream stream(&_csvLogFile);
3728 QStringList allFactNames;
3729 allFactNames << factNames();
3730 for (const QString& groupName: factGroupNames()) {
3731 for(const QString& factName: getFactGroup(groupName)->factNames()){
3732 allFactNames << QString("%1.%2").arg(groupName, factName);
3733 }
3734 }
3735 qCDebug(VehicleLog) << "Facts logged to csv:" << allFactNames;
3736 stream << "Timestamp," << allFactNames.join(",") << "\n";
3737}
3738
3739void Vehicle::_writeCsvLine()
3740{
3741 // Only save the logs after the the vehicle gets armed, unless "Save logs even if vehicle was not armed" is checked
3742 if(!_csvLogFile.isOpen() &&
3743 (_armed || SettingsManager::instance()->mavlinkSettings()->telemetrySaveNotArmed()->rawValue().toBool())){
3744 _initializeCsv();
3745 }
3746
3747 if(!_csvLogFile.isOpen()){
3748 return;
3749 }
3750
3751 QStringList allFactValues;
3752 QTextStream stream(&_csvLogFile);
3753
3754 // Write timestamp to csv file
3755 allFactValues << QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd hh:mm:ss.zzz"));
3756 // Write Vehicle's own facts
3757 for (const QString& factName : factNames()) {
3758 allFactValues << getFact(factName)->cookedValueString();
3759 }
3760 // write facts from Vehicle's FactGroups
3761 for (const QString& groupName: factGroupNames()) {
3762 for (const QString& factName : getFactGroup(groupName)->factNames()) {
3763 allFactValues << getFactGroup(groupName)->getFact(factName)->cookedValueString();
3764 }
3765 }
3766
3767 stream << allFactValues.join(",") << "\n";
3768}
3769
3770void Vehicle::doSetHome(const QGeoCoordinate& coord)
3771{
3772 if (coord.isValid()) {
3773 // If for some reason we already did a query and it hasn't arrived yet, disconnect signals and unset current query. TerrainQuery system will
3774 // automatically delete that forgotten query. This could happen if we send 2 do_set_home commands one after another, so usually the latest one
3775 // is the one we would want to arrive to the vehicle, so it is fine to forget about the previous ones in case it could happen.
3777 disconnect(_currentDoSetHomeTerrainAtCoordinateQuery, &TerrainAtCoordinateQuery::terrainDataReceived, this, &Vehicle::_doSetHomeTerrainReceived);
3779 }
3780 // Save the coord for using when our terrain data arrives. If there was a pending terrain query paired with an older coordinate it is safe to
3781 // Override now, as we just disconnected the signal that would trigger the command sending
3782 _doSetHomeCoordinate = coord;
3783 // Now setup and trigger the new terrain query
3785 connect(_currentDoSetHomeTerrainAtCoordinateQuery, &TerrainAtCoordinateQuery::terrainDataReceived, this, &Vehicle::_doSetHomeTerrainReceived);
3786 QList<QGeoCoordinate> rgCoord;
3787 rgCoord.append(coord);
3789 }
3790}
3791
3792// This will be called after our query started in doSetHome arrives
3793void Vehicle::_doSetHomeTerrainReceived(bool success, QList<double> heights)
3794{
3795 if (success) {
3796 double terrainAltitude = heights[0];
3797 // Coord and terrain alt sanity check
3798 if (_doSetHomeCoordinate.isValid() && terrainAltitude <= SET_HOME_TERRAIN_ALT_MAX && terrainAltitude >= SET_HOME_TERRAIN_ALT_MIN) {
3801 MAV_CMD_DO_SET_HOME,
3802 true, // show error if fails
3803 0,
3804 0,
3805 0,
3806 static_cast<float>(qQNaN()),
3807 _doSetHomeCoordinate.latitude(),
3808 _doSetHomeCoordinate.longitude(),
3809 terrainAltitude);
3810
3811 } else if (_doSetHomeCoordinate.isValid()) {
3812 qCDebug(VehicleLog) << "_doSetHomeTerrainReceived: internal error, elevation data out of limits ";
3813 } else {
3814 qCDebug(VehicleLog) << "_doSetHomeTerrainReceived: internal error, cached home coordinate is not valid";
3815 }
3816 } else {
3817 qgcApp()->showAppMessage(tr("Set Home failed, terrain data not available for selected coordinate"));
3818 }
3819 // Clean up
3821 _doSetHomeCoordinate = QGeoCoordinate(); // So isValid() will no longer return true, for extra safety
3822}
3823
3824void Vehicle::_updateAltAboveTerrain()
3825{
3826 // We won't do another query if the previous query was done closer than 2 meters from current position
3827 // or if altitude change has been less than 0.5 meters since then.
3828 const qreal minimumDistanceTraveled = 2;
3829 const float minimumAltitudeChanged = 0.5f;
3830
3831 // This is not super elegant but it works to limit the amount of queries we do. It seems more than 500ms is not possible to get
3832 // serviced on time. It is not a big deal if it is not serviced on time as terrain queries can manage that just fine, but QGC would
3833 // use resources to service those queries, and it is pointless, so this is a quick workaround to not waste that little computing time
3834 int altitudeAboveTerrQueryMinInterval = 500;
3835 if (_altitudeAboveTerrQueryTimer.elapsed() < altitudeAboveTerrQueryMinInterval) {
3836 // qCDebug(VehicleLog) << "_updateAltAboveTerrain: minimum 500ms interval between queries not reached, returning";
3837 return;
3838 }
3839 // Sanity check, although it is very unlikely that vehicle coordinate is not valid
3840 if (_coordinate.isValid()) {
3841 // Check for minimum distance and altitude traveled before doing query, to not do a lot of queries of the same data
3843 if (_altitudeAboveTerrLastCoord.distanceTo(_coordinate) < minimumDistanceTraveled && fabs(_altitudeRelativeFact.rawValue().toFloat() - _altitudeAboveTerrLastRelAlt) < minimumAltitudeChanged ) {
3844 return;
3845 }
3846 }
3847 _altitudeAboveTerrLastCoord = _coordinate;
3849
3850 // If for some reason we already did a query and it hasn't arrived yet, disconnect signals and unset current query. TerrainQuery system will
3851 // automatically delete that forgotten query.
3853 // qCDebug(VehicleLog) << "_updateAltAboveTerrain: cleaning previous query, no longer needed";
3854 disconnect(_altitudeAboveTerrTerrainAtCoordinateQuery, &TerrainAtCoordinateQuery::terrainDataReceived, this, &Vehicle::_altitudeAboveTerrainReceived);
3856 }
3857 // Now setup and trigger the new terrain query
3859 connect(_altitudeAboveTerrTerrainAtCoordinateQuery, &TerrainAtCoordinateQuery::terrainDataReceived, this, &Vehicle::_altitudeAboveTerrainReceived);
3860 QList<QGeoCoordinate> rgCoord;
3861 rgCoord.append(_coordinate);
3864 }
3865}
3866
3867void Vehicle::_altitudeAboveTerrainReceived(bool success, QList<double> heights)
3868{
3869 if (!success) {
3870 qCDebug(VehicleLog) << "_altitudeAboveTerrainReceived: terrain data not available for vehicle coordinate";
3871 } else {
3872 // Query was succesful, save the data.
3873 double terrainAltitude = heights[0];
3874 double altitudeAboveTerrain = altitudeAMSL()->rawValue().toDouble() - terrainAltitude;
3875 _altitudeAboveTerrFact.setRawValue(altitudeAboveTerrain);
3876 }
3877 // Clean up
3879}
3880
3881void Vehicle::_handleObstacleDistance(const mavlink_message_t& message)
3882{
3883 mavlink_obstacle_distance_t o;
3884 mavlink_msg_obstacle_distance_decode(&message, &o);
3885 _objectAvoidance->update(&o);
3886}
3887
3888void Vehicle::_handleFenceStatus(const mavlink_message_t& message)
3889{
3890 mavlink_fence_status_t fenceStatus;
3891
3892 mavlink_msg_fence_status_decode(&message, &fenceStatus);
3893
3894 qCDebug(VehicleLog) << "_handleFenceStatus breach_status" << fenceStatus.breach_status;
3895
3896 static qint64 lastUpdate = 0;
3897 qint64 now = QDateTime::currentMSecsSinceEpoch();
3898 if (fenceStatus.breach_status == 1) {
3899 if (now - lastUpdate > 3000) {
3900 lastUpdate = now;
3901 QString breachTypeStr;
3902 switch (fenceStatus.breach_type) {
3903 case FENCE_BREACH_NONE:
3904 return;
3905 case FENCE_BREACH_MINALT:
3906 breachTypeStr = tr("minimum altitude");
3907 break;
3908 case FENCE_BREACH_MAXALT:
3909 breachTypeStr = tr("maximum altitude");
3910 break;
3911 case FENCE_BREACH_BOUNDARY:
3912 breachTypeStr = tr("boundary");
3913 break;
3914 default:
3915 break;
3916 }
3917
3918 _say(breachTypeStr + " " + tr("fence breached"));
3919 }
3920 } else {
3921 lastUpdate = now;
3922 }
3923}
3924
3926{
3927 _flightDistanceFact.setRawValue(_flightDistanceFact.rawValue().toDouble() + distance);
3928}
3929
3930void Vehicle::sendParamMapRC(const QString& paramName, double scale, double centerValue, int tuningID, double minValue, double maxValue)
3931{
3932 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
3933 if (!sharedLink) {
3934 qCDebug(VehicleLog) << "sendParamMapRC: primary link gone!";
3935 return;
3936 }
3937
3938 mavlink_message_t message;
3939
3940 char param_id_cstr[MAVLINK_MSG_PARAM_MAP_RC_FIELD_PARAM_ID_LEN] = {};
3941 // Copy string into buffer, ensuring not to exceed the buffer size
3942 for (unsigned int i = 0; i < sizeof(param_id_cstr); i++) {
3943 if ((int)i < paramName.length()) {
3944 param_id_cstr[i] = paramName.toLatin1()[i];
3945 }
3946 }
3947
3948 mavlink_msg_param_map_rc_pack_chan(static_cast<uint8_t>(MAVLinkProtocol::instance()->getSystemId()),
3949 static_cast<uint8_t>(MAVLinkProtocol::getComponentId()),
3950 sharedLink->mavlinkChannel(),
3951 &message,
3952 _systemID,
3953 MAV_COMP_ID_AUTOPILOT1,
3954 param_id_cstr,
3955 -1, // parameter name specified as string in previous argument
3956 static_cast<uint8_t>(tuningID),
3957 static_cast<float>(centerValue),
3958 static_cast<float>(scale),
3959 static_cast<float>(minValue),
3960 static_cast<float>(maxValue));
3961 sendMessageOnLinkThreadSafe(sharedLink.get(), message);
3962}
3963
3965{
3966 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
3967 if (!sharedLink) {
3968 qCDebug(VehicleLog)<< "clearAllParamMapRC: primary link gone!";
3969 return;
3970 }
3971
3972 char param_id_cstr[MAVLINK_MSG_PARAM_MAP_RC_FIELD_PARAM_ID_LEN] = {};
3973
3974 for (int i = 0; i < 3; i++) {
3975 mavlink_message_t message;
3976 mavlink_msg_param_map_rc_pack_chan(static_cast<uint8_t>(MAVLinkProtocol::instance()->getSystemId()),
3977 static_cast<uint8_t>(MAVLinkProtocol::getComponentId()),
3978 sharedLink->mavlinkChannel(),
3979 &message,
3980 _systemID,
3981 MAV_COMP_ID_AUTOPILOT1,
3982 param_id_cstr,
3983 -2, // Disable map for specified tuning id
3984 i, // tuning id
3985 0, 0, 0, 0); // unused
3986 sendMessageOnLinkThreadSafe(sharedLink.get(), message);
3987 }
3988}
3989
3990void Vehicle::sendJoystickDataThreadSafe(float roll, float pitch, float yaw, float thrust, quint16 buttons, quint16 buttons2, float pitchExtension, float rollExtension, float aux1, float aux2, float aux3, float aux4, float aux5, float aux6)
3991{
3992 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
3993 if (!sharedLink) {
3994 qCDebug(VehicleLog)<< "sendJoystickDataThreadSafe: primary link gone!";
3995 return;
3996 }
3997
3998 if (sharedLink->linkConfiguration()->isHighLatency()) {
3999 return;
4000 }
4001
4002 mavlink_message_t message;
4003
4004 float axesScaling = 1.0 * 1000.0;
4005 uint8_t extensions = 0;
4006
4007 // Incoming values are in the range -1:1
4008 float newRollCommand = roll * axesScaling;
4009 float newPitchCommand = pitch * axesScaling;
4010 float newYawCommand = yaw * axesScaling;
4011 float newThrustCommand = thrust * axesScaling;
4012
4013 // Scale and set extension bits/values
4014 float incomingExtensionValues[] = { pitchExtension, rollExtension, aux1, aux2, aux3, aux4, aux5, aux6 };
4015 int16_t outgoingExtensionValues[std::size(incomingExtensionValues)];
4016 for (size_t i = 0; i < std::size(incomingExtensionValues); i++) {
4017 int16_t scaledValue = 0;
4018 if (!qIsNaN(incomingExtensionValues[i])) {
4019 scaledValue = static_cast<int16_t>(incomingExtensionValues[i] * axesScaling);
4020 extensions |= (1 << i);
4021 }
4022 outgoingExtensionValues[i] = scaledValue;
4023 }
4024 mavlink_msg_manual_control_pack_chan(
4025 static_cast<uint8_t>(MAVLinkProtocol::instance()->getSystemId()),
4026 static_cast<uint8_t>(MAVLinkProtocol::getComponentId()),
4027 sharedLink->mavlinkChannel(),
4028 &message,
4029 static_cast<uint8_t>(_systemID),
4030 static_cast<int16_t>(newPitchCommand),
4031 static_cast<int16_t>(newRollCommand),
4032 static_cast<int16_t>(newThrustCommand),
4033 static_cast<int16_t>(newYawCommand),
4034 buttons, buttons2,
4035 extensions,
4036 outgoingExtensionValues[0],
4037 outgoingExtensionValues[1],
4038 outgoingExtensionValues[2],
4039 outgoingExtensionValues[3],
4040 outgoingExtensionValues[4],
4041 outgoingExtensionValues[5],
4042 outgoingExtensionValues[6],
4043 outgoingExtensionValues[7]
4044 );
4045 sendMessageOnLinkThreadSafe(sharedLink.get(), message);
4046}
4047
4049{
4050 sendMavCommand(_defaultComponentId,
4051 MAV_CMD_DO_DIGICAM_CONTROL,
4052 true, // show errors
4053 0.0, 0.0, 0.0, 0.0, // param 1-4 unused
4054 1.0); // trigger camera
4055}
4056
4058{
4060 _defaultComponentId,
4061 MAV_CMD_DO_GRIPPER,
4062 true, // Show errors
4063 0, // Param1: Gripper ID (Always set to 0)
4064 gripperAction); // Param2: Gripper Action
4065}
4066
4067void Vehicle::setEstimatorOrigin(const QGeoCoordinate& centerCoord)
4068{
4069 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
4070 if (!sharedLink) {
4071 qCDebug(VehicleLog) << "setEstimatorOrigin: primary link gone!";
4072 return;
4073 }
4074
4076 mavlink_msg_set_gps_global_origin_pack_chan(
4077 MAVLinkProtocol::instance()->getSystemId(),
4079 sharedLink->mavlinkChannel(),
4080 &msg,
4081 id(),
4082 centerCoord.latitude() * 1e7,
4083 centerCoord.longitude() * 1e7,
4084 centerCoord.altitude() * 1e3,
4085 static_cast<float>(qQNaN())
4086 );
4087 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
4088}
4089
4090void Vehicle::pairRX(int rxType, int rxSubType)
4091{
4092 sendMavCommand(_defaultComponentId,
4093 MAV_CMD_START_RX_PAIR,
4094 true,
4095 rxType,
4096 rxSubType);
4097}
4098
4099void Vehicle::_handleMessageInterval(const mavlink_message_t& message)
4100{
4101 mavlink_message_interval_t data;
4102 mavlink_msg_message_interval_decode(&message, &data);
4103
4104 const MavCompMsgId compMsgId = {message.compid, data.message_id};
4105 const int32_t rate = ( data.interval_us > 0 ) ? 1000000.0 / data.interval_us : data.interval_us;
4106
4107 if(!_mavlinkMsgIntervals.contains(compMsgId) || _mavlinkMsgIntervals.value(compMsgId) != rate)
4108 {
4109 (void) _mavlinkMsgIntervals.insert(compMsgId, rate);
4110 emit mavlinkMsgIntervalsChanged(message.compid, data.message_id, rate);
4111 }
4112}
4113
4115{
4116 _timerRevertAllowTakeover.stop();
4117 _timerRevertAllowTakeover.setSingleShot(true);
4118 _timerRevertAllowTakeover.setInterval(operatorControlTakeoverTimeoutMsecs());
4119 // Disconnect any previous connections to avoid multiple handlers
4120 disconnect(&_timerRevertAllowTakeover, &QTimer::timeout, nullptr, nullptr);
4121
4122 connect(&_timerRevertAllowTakeover, &QTimer::timeout, this, [this](){
4123 if (MAVLinkProtocol::instance()->getSystemId() == _sysid_in_control) {
4124 this->requestOperatorControl(false);
4125 }
4126 });
4127 _timerRevertAllowTakeover.start();
4128}
4129
4130void Vehicle::requestOperatorControl(bool allowOverride, int requestTimeoutSecs)
4131{
4132 int safeRequestTimeoutSecs;
4133 int requestTimeoutSecsMin = SettingsManager::instance()->flyViewSettings()->requestControlTimeout()->cookedMin().toInt();
4134 int requestTimeoutSecsMax = SettingsManager::instance()->flyViewSettings()->requestControlTimeout()->cookedMax().toInt();
4135 if (requestTimeoutSecs >= requestTimeoutSecsMin && requestTimeoutSecs <= requestTimeoutSecsMax) {
4136 safeRequestTimeoutSecs = requestTimeoutSecs;
4137 } else {
4138 // If out of limits use default value
4139 safeRequestTimeoutSecs = SettingsManager::instance()->flyViewSettings()->requestControlTimeout()->cookedDefaultValue().toInt();
4140 }
4141
4142 const MavCmdAckHandlerInfo_t handlerInfo = {&Vehicle::_requestOperatorControlAckHandler, this, nullptr, nullptr};
4144 &handlerInfo,
4145 _defaultComponentId,
4146 MAV_CMD_REQUEST_OPERATOR_CONTROL,
4147 0, // System ID of GCS requesting control, 0 if it is this GCS
4148 1, // Action - 0: Release control, 1: Request control.
4149 allowOverride ? 1 : 0, // Allow takeover - Enable automatic granting of ownership on request. 0: Ask current owner and reject request, 1: Allow automatic takeover.
4150 safeRequestTimeoutSecs // Timeout in seconds before a request to a GCS to allow takeover is assumed to be rejected. This is used to display the timeout graphically on requestor and GCS in control.
4151 );
4152
4153 // If this is a request we sent to other GCS, start timer so User can not keep sending requests until the current timeout expires
4154 if (requestTimeoutSecs > 0) {
4155 requestOperatorControlStartTimer(requestTimeoutSecs * 1000);
4156 }
4157}
4158
4159void Vehicle::_requestOperatorControlAckHandler(void* resultHandlerData, int compId, const mavlink_command_ack_t& ack, MavCmdResultFailureCode_t failureCode)
4160{
4161 // For the moment, this will always come from an autopilot, compid 1
4162 Q_UNUSED(compId);
4163
4164 // If duplicated or no response, show popup to user. Otherwise only log it.
4165 switch (failureCode) {
4167 qgcApp()->showAppMessage(tr("Waiting for previous operator control request"));
4168 return;
4170 qgcApp()->showAppMessage(tr("No response to operator control request"));
4171 return;
4172 default:
4173 break;
4174 }
4175
4176 Vehicle* vehicle = static_cast<Vehicle*>(resultHandlerData);
4177 if (!vehicle) {
4178 return;
4179 }
4180
4181 if (ack.result == MAV_RESULT_ACCEPTED) {
4182 qCDebug(VehicleLog) << "Operator control request accepted";
4183 } else {
4184 qCDebug(VehicleLog) << "Operator control request rejected";
4185 }
4186}
4187
4188void Vehicle::requestOperatorControlStartTimer(int requestTimeoutMsecs)
4189{
4190 // First flag requests not allowed
4191 _sendControlRequestAllowed = false;
4193 // Setup timer to re enable it again after timeout
4194 _timerRequestOperatorControl.stop();
4195 _timerRequestOperatorControl.setSingleShot(true);
4196 _timerRequestOperatorControl.setInterval(requestTimeoutMsecs);
4197 // Disconnect any previous connections to avoid multiple handlers
4198 disconnect(&_timerRequestOperatorControl, &QTimer::timeout, nullptr, nullptr);
4199 connect(&_timerRequestOperatorControl, &QTimer::timeout, this, [this](){
4200 _sendControlRequestAllowed = true;
4202 });
4203 _timerRequestOperatorControl.start();
4204}
4205
4206void Vehicle::_handleControlStatus(const mavlink_message_t& message)
4207{
4208 mavlink_control_status_t controlStatus;
4209 mavlink_msg_control_status_decode(&message, &controlStatus);
4210
4211 bool updateControlStatusSignals = false;
4212 if (_gcsControlStatusFlags != controlStatus.flags) {
4213 _gcsControlStatusFlags = controlStatus.flags;
4214 _gcsControlStatusFlags_SystemManager = controlStatus.flags & GCS_CONTROL_STATUS_FLAGS_SYSTEM_MANAGER;
4215 _gcsControlStatusFlags_TakeoverAllowed = controlStatus.flags & GCS_CONTROL_STATUS_FLAGS_TAKEOVER_ALLOWED;
4216 updateControlStatusSignals = true;
4217 }
4218
4219 if (_sysid_in_control != controlStatus.sysid_in_control) {
4220 _sysid_in_control = controlStatus.sysid_in_control;
4221 updateControlStatusSignals = true;
4222 }
4223
4224 if (!_firstControlStatusReceived) {
4225 _firstControlStatusReceived = true;
4226 updateControlStatusSignals = true;
4227 }
4228
4229 if (updateControlStatusSignals) {
4231 }
4232
4233 // If we were waiting for a request to be accepted and now it was accepted, adjust flags accordingly so
4234 // UI unlocks the request/take control button
4235 if (!sendControlRequestAllowed() && _gcsControlStatusFlags_TakeoverAllowed) {
4236 disconnect(&_timerRequestOperatorControl, &QTimer::timeout, nullptr, nullptr);
4237 _sendControlRequestAllowed = true;
4239 }
4240}
4241
4242void Vehicle::_handleCommandRequestOperatorControl(const mavlink_command_long_t commandLong)
4243{
4244 emit requestOperatorControlReceived(commandLong.param1, commandLong.param3, commandLong.param4);
4245}
4246
4247void Vehicle::_handleCommandLong(const mavlink_message_t& message)
4248{
4249 mavlink_command_long_t commandLong;
4250 mavlink_msg_command_long_decode(&message, &commandLong);
4251 // Ignore command if it is not targeted for us
4252 if (commandLong.target_system != MAVLinkProtocol::instance()->getSystemId()) {
4253 return;
4254 }
4255 if (commandLong.command == MAV_CMD_REQUEST_OPERATOR_CONTROL) {
4256 _handleCommandRequestOperatorControl(commandLong);
4257 }
4258}
4259
4260int Vehicle::operatorControlTakeoverTimeoutMsecs() const
4261{
4263}
4264
4265void Vehicle::_requestMessageMessageIntervalResultHandler(void* resultHandlerData, MAV_RESULT result, RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t& message)
4266{
4267 if((result != MAV_RESULT_ACCEPTED) || (failureCode != RequestMessageNoFailure))
4268 {
4269 mavlink_message_interval_t data;
4270 mavlink_msg_message_interval_decode(&message, &data);
4271
4272 Vehicle* vehicle = static_cast<Vehicle*>(resultHandlerData);
4273 (void) vehicle->_unsupportedMessageIds.insert(message.compid, data.message_id);
4274 }
4275}
4276
4277void Vehicle::_requestMessageInterval(uint8_t compId, uint16_t msgId)
4278{
4279 if(!_unsupportedMessageIds.contains(compId, msgId))
4280 {
4282 &Vehicle::_requestMessageMessageIntervalResultHandler,
4283 this,
4284 compId,
4285 MAVLINK_MSG_ID_MESSAGE_INTERVAL,
4286 msgId
4287 );
4288 }
4289}
4290
4291int32_t Vehicle::getMessageRate(uint8_t compId, uint16_t msgId)
4292{
4293 // TODO: Use QGCMavlinkMessage
4294 const MavCompMsgId compMsgId = {compId, msgId};
4295 int32_t rate = 0;
4296 if(_mavlinkMsgIntervals.contains(compMsgId))
4297 {
4298 rate = _mavlinkMsgIntervals.value(compMsgId);
4299 }
4300 else
4301 {
4302 _requestMessageInterval(compId, msgId);
4303 }
4304 return rate;
4305}
4306
4307void Vehicle::_setMessageRateCommandResultHandler(void* resultHandlerData, int compId, const mavlink_command_ack_t& ack, MavCmdResultFailureCode_t failureCode)
4308{
4309 if((ack.result == MAV_RESULT_ACCEPTED) && (failureCode == MavCmdResultCommandResultOnly))
4310 {
4311 Vehicle* vehicle = static_cast<Vehicle*>(resultHandlerData);
4312 if(vehicle)
4313 {
4314 vehicle->_requestMessageInterval(compId, vehicle->_lastSetMsgIntervalMsgId);
4315 }
4316 }
4317}
4318
4319void Vehicle::setMessageRate(uint8_t compId, uint16_t msgId, int32_t rate)
4320{
4321 const MavCmdAckHandlerInfo_t handlerInfo = {
4322 /* .resultHandler = */ &Vehicle::_setMessageRateCommandResultHandler,
4323 /* .resultHandlerData = */ this,
4324 /* .progressHandler = */ nullptr,
4325 /* .progressHandlerData = */ nullptr
4326 };
4327
4328 const float interval = (rate > 0) ? 1000000.0 / rate : rate;
4329 _lastSetMsgIntervalMsgId = msgId;
4330
4332 &handlerInfo,
4333 compId,
4334 MAV_CMD_SET_MESSAGE_INTERVAL,
4335 msgId,
4336 interval
4337 );
4338}
4339
4340QVariant Vehicle::expandedToolbarIndicatorSource(const QString& indicatorName)
4341{
4342 return _firmwarePlugin->expandedToolbarIndicatorSource(this, indicatorName);
4343}
4344
4346{
4347 switch(failureCode)
4348 {
4350 return QStringLiteral("No Failure");
4352 return QStringLiteral("Command Error");
4354 return QStringLiteral("Command Not Acked");
4356 return QStringLiteral("Message Not Received");
4358 return QStringLiteral("Duplicate Request");
4359 default:
4360 return QStringLiteral("Unknown (%1)").arg(failureCode);
4361 }
4362}
4363
4365{
4366 switch(failureCode)
4367 {
4369 return QStringLiteral("Command Result Only");
4371 return QStringLiteral("No Response To Command");
4373 return QStringLiteral("Duplicate Command");
4374 default:
4375 return QStringLiteral("Unknown (%1)").arg(failureCode);
4376 }
4377}
4378
4379/*===========================================================================*/
4380/* ardupilotmega Dialect */
4381/*===========================================================================*/
4382
4384{
4385 if (apmFirmware()) {
4388 MAV_CMD_FLASH_BOOTLOADER,
4389 true, // show error
4390 0, 0, 0, 0, // param 1-4 not used
4391 290876); // magic number
4392 }
4393}
4394
4396{
4397 if (apmFirmware()) {
4400 MAV_CMD_DO_AUX_FUNCTION,
4401 true,
4403 enable ? MAV_CMD_DO_AUX_FUNCTION_SWITCH_LEVEL_HIGH : MAV_CMD_DO_AUX_FUNCTION_SWITCH_LEVEL_LOW);
4404 }
4405}
4406
4407/*---------------------------------------------------------------------------*/
4408/*===========================================================================*/
4409/* Status Text Handler */
4410/*===========================================================================*/
4411
4412void Vehicle::resetAllMessages() { m_statusTextHandler->resetAllMessages(); }
4413void Vehicle::resetErrorLevelMessages() { m_statusTextHandler->resetErrorLevelMessages(); }
4414void Vehicle::clearMessages() { m_statusTextHandler->clearMessages(); }
4415bool Vehicle::messageTypeNone() const { return m_statusTextHandler->messageTypeNone(); }
4416bool Vehicle::messageTypeNormal() const { return m_statusTextHandler->messageTypeNormal(); }
4417bool Vehicle::messageTypeWarning() const { return m_statusTextHandler->messageTypeWarning(); }
4418bool Vehicle::messageTypeError() const { return m_statusTextHandler->messageTypeError(); }
4419int Vehicle::messageCount() const { return m_statusTextHandler->messageCount(); }
4420QString Vehicle::formattedMessages() const { return m_statusTextHandler->formattedMessages(); }
4421
4422void Vehicle::_createStatusTextHandler()
4423{
4424 m_statusTextHandler = new StatusTextHandler(this);
4425 (void) connect(m_statusTextHandler, &StatusTextHandler::messageTypeChanged, this, &Vehicle::messageTypeChanged);
4426 (void) connect(m_statusTextHandler, &StatusTextHandler::messageCountChanged, this, &Vehicle::messageCountChanged);
4427 (void) connect(m_statusTextHandler, &StatusTextHandler::newFormattedMessage, this, &Vehicle::newFormattedMessage);
4428 (void) connect(m_statusTextHandler, &StatusTextHandler::textMessageReceived, this, &Vehicle::_textMessageReceived);
4429 (void) connect(m_statusTextHandler, &StatusTextHandler::newErrorMessage, this, &Vehicle::_errorMessageReceived);
4430}
4431
4432void Vehicle::_textMessageReceived(MAV_COMPONENT componentid, MAV_SEVERITY severity, QString text, QString description)
4433{
4434 // PX4 backwards compatibility: messages sent out ending with a tab are also sent as event
4435 if (px4Firmware() && text.endsWith('\t')) {
4436 qCDebug(VehicleLog) << "Dropping message (expected as event):" << text;
4437 return;
4438 }
4439
4440 bool skipSpoken = false;
4441 const bool ardupilotPrearm = text.startsWith(QStringLiteral("PreArm"));
4442 const bool px4Prearm = text.startsWith(QStringLiteral("preflight"), Qt::CaseInsensitive) && (severity >= MAV_SEVERITY::MAV_SEVERITY_CRITICAL);
4443 if (ardupilotPrearm || px4Prearm) {
4444 auto eventData = _events.find(componentid);
4445 if (eventData != _events.end()) {
4446 if (eventData->data()->healthAndArmingChecksSupported()) {
4447 qCDebug(VehicleLog) << "Dropping preflight message (expected as event):" << text;
4448 return;
4449 }
4450 }
4451
4452 // Limit repeated PreArm message to once every 10 seconds
4453 if (_noisySpokenPrearmMap.contains(text) && _noisySpokenPrearmMap.value(text).msecsTo(QTime::currentTime()) < (10 * 1000)) {
4454 skipSpoken = true;
4455 } else {
4456 (void) _noisySpokenPrearmMap.insert(text, QTime::currentTime());
4457 setPrearmError(text);
4458 }
4459 }
4460
4461 bool readAloud = false;
4462
4463 if (text.startsWith("#")) {
4464 (void) text.remove(0, 1);
4465 readAloud = true;
4466 } else if (severity <= MAV_SEVERITY::MAV_SEVERITY_NOTICE) {
4467 readAloud = true;
4468 }
4469
4470 if (readAloud && !skipSpoken) {
4471 _say(text);
4472 }
4473
4474 emit textMessageReceived(id(), componentid, severity, text, description);
4475 m_statusTextHandler->handleHTMLEscapedTextMessage(componentid, severity, text.toHtmlEscaped(), description);
4476}
4477
4478void Vehicle::_errorMessageReceived(QString message)
4479{
4480 if (_isActiveVehicle) {
4481 qgcApp()->showCriticalVehicleMessage(message);
4482 }
4483}
4484
4485/*---------------------------------------------------------------------------*/
4486/*===========================================================================*/
4487/* Signing */
4488/*===========================================================================*/
4489
4491{
4492 SharedLinkInterfacePtr sharedLink = vehicleLinkManager()->primaryLink().lock();
4493 if (!sharedLink) {
4494 qCDebug(VehicleLog) << "Primary Link Gone!";
4495 return;
4496 }
4497
4498 const mavlink_channel_t channel = static_cast<mavlink_channel_t>(sharedLink->mavlinkChannel());
4499
4500 mavlink_setup_signing_t setup_signing;
4501
4502 mavlink_system_t target_system;
4503 target_system.sysid = id();
4504 target_system.compid = defaultComponentId();
4505
4506 MAVLinkSigning::createSetupSigning(channel, target_system, setup_signing);
4507
4509 (void) mavlink_msg_setup_signing_encode_chan(MAVLinkProtocol::instance()->getSystemId(), MAVLinkProtocol::getComponentId(), channel, &msg, &setup_signing);
4510
4511 // Since we don't get an ack back that the message was received send twice to try to make sure it makes it to the vehicle
4512 for (uint8_t i = 0; i < 2; ++i) {
4513 sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
4514 }
4515}
4516
4517/*---------------------------------------------------------------------------*/
4518/*===========================================================================*/
4519/* Image Protocol Manager */
4520/*===========================================================================*/
4521
4522void Vehicle::_createImageProtocolManager()
4523{
4524 _imageProtocolManager = new ImageProtocolManager(this);
4525 (void) connect(_imageProtocolManager, &ImageProtocolManager::flowImageIndexChanged, this, &Vehicle::flowImageIndexChanged);
4526 (void) connect(_imageProtocolManager, &ImageProtocolManager::imageReady, this, [this](const QImage &image) {
4527 qgcApp()->qgcImageProvider()->setImage(image, _systemID);
4528 });
4529}
4530
4532{
4533 return (_imageProtocolManager ? _imageProtocolManager->flowImageIndex() : 0);
4534}
4535
4536/*---------------------------------------------------------------------------*/
4537/*===========================================================================*/
4538/* MAVLink Log Manager */
4539/*===========================================================================*/
4540
4541void Vehicle::_createMAVLinkLogManager()
4542{
4543 _mavlinkLogManager = new MAVLinkLogManager(this, this);
4544}
4545
4547{
4548 return _mavlinkLogManager;
4549}
4550
4551/*---------------------------------------------------------------------------*/
4552/*===========================================================================*/
4553/* Camera Manager */
4554/*===========================================================================*/
4555
4556void Vehicle::_createCameraManager()
4557{
4558 if (!_cameraManager && _firmwarePlugin) {
4559 _cameraManager = _firmwarePlugin->createCameraManager(this);
4560 emit cameraManagerChanged();
4561 }
4562}
4563
4564const QVariantList &Vehicle::staticCameraList() const
4565{
4566 if (_cameraManager) {
4567 return _cameraManager->cameraList();
4568 }
4569
4570 static QVariantList emptyCameraList;
4571 return emptyCameraList;
4572}
4573
4574/*---------------------------------------------------------------------------*/
static const QString guided_mode_not_supported_by_vehicle
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
mavlink_channel_t
Definition MAVLinkLib.h:7
#define qgcApp()
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static QString placeholders(int n)
#define SET_HOME_TERRAIN_ALT_MIN
Definition Vehicle.cc:65
static void _sendMavCommandWithLambdaFallbackHandler(void *resultHandlerData, int, const mavlink_command_ack_t &ack, Vehicle::MavCmdResultFailureCode_t)
Definition Vehicle.cc:2378
#define REQUEST_OPERATOR_CONTROL_ALLOW_TAKEOVER_TIMEOUT_MSECS
Definition Vehicle.cc:69
const QString guided_mode_not_supported_by_vehicle
Definition Vehicle.cc:71
void mavlinkMessageReceived(const mavlink_message_t &message)
static ADSBVehicleManager * instance()
void load(const QString &json_file)
Definition Actuators.cc:110
void say(const QString &text, TextMods textMods=TextMod::None)
static AudioOutput * instance()
void handleEvents(const mavlink_message_t &message)
void healthAndArmingChecksUpdated()
void setMetadata(const QString &metadataJsonFileName)
int getModeGroup(int32_t customMode)
void handleMessageForFactGroupCreation(Vehicle *vehicle, const mavlink_message_t &message)
Allows for creation/updating of dynamic FactGroups based on incoming messages.
Used to group Facts together into an object hierarachy.
Definition FactGroup.h:19
void _addFactGroup(FactGroup *factGroup, const QString &name)
Definition FactGroup.cc:130
static QVariant metersToAppSettingsHorizontalDistanceUnits(const QVariant &meters)
Converts from meters to the user specified horizontal distance unit.
static QString appSettingsHorizontalDistanceUnitsString()
Returns the string for horizontal distance units which has configued by user.
A Fact is used to hold a single value within the system.
Definition Fact.h:19
void rawValueChanged(const QVariant &value)
void setCommandSupported(MAV_CMD cmd, CommandSupportedResult status)
FirmwarePlugin * firmwarePluginForAutopilot(MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType)
static FirmwarePluginManager * instance()
virtual QString motorDetectionFlightMode() const
Returns the flight mode for Motor Detection.
virtual QString pauseFlightMode() const
Returns The flight mode which indicates the vehicle is paused.
virtual QString brandImageOutdoor(const Vehicle *) const
Return the resource file which contains the brand image for the vehicle for Outdoor theme.
virtual void guidedModeChangeEquivalentAirspeedMetersSecond(Vehicle *vehicle, double airspeed_equiv) const
virtual void guidedModeChangeAltitude(Vehicle *vehicle, double altitudeChange, bool pauseVehicle)
virtual void startMission(Vehicle *vehicle) const
Command the vehicle to start the mission.
virtual double maximumHorizontalSpeedMultirotor(Vehicle *) const
virtual bool multiRotorCoaxialMotors(Vehicle *) const
virtual double minimumEquivalentAirspeed(Vehicle *) const
virtual const QVariantList & toolIndicators(const Vehicle *vehicle)
virtual void guidedModeChangeGroundSpeedMetersSecond(Vehicle *vehicle, double groundspeed) const
virtual bool setFlightMode(const QString &flightMode, uint8_t *base_mode, uint32_t *custom_mode) const
@ SetFlightModeCapability
FirmwarePlugin::setFlightMode method is supported.
virtual QString vehicleImageOutline(const Vehicle *) const
Return the resource file which contains the vehicle icon used in the flight view when the view is lig...
virtual void guidedModeTakeoff(Vehicle *vehicle, double takeoffAltRel) const
Command vehicle to takeoff from current location to the specified height.
virtual bool isCapable(const Vehicle *, FirmwareCapabilities) const
virtual double maximumEquivalentAirspeed(Vehicle *) const
virtual QString missionFlightMode() const
Returns the flight mode for running missions.
virtual void adjustMetaData(MAV_TYPE, FactMetaData *)
virtual bool adjustIncomingMavlinkMessage(Vehicle *, mavlink_message_t *)
virtual bool fixedWingAirSpeedLimitsAvailable(Vehicle *) const
virtual void guidedModeChangeHeading(Vehicle *vehicle, const QGeoCoordinate &headingCoord) const
Command vehicle to rotate towards specified location.
virtual QString smartRTLFlightMode() const
Returns the flight mode for Smart RTL.
virtual bool multiRotorXConfig(Vehicle *) const
virtual QString takeControlFlightMode() const
Returns the flight mode to use when the operator wants to take back control from autonomouse flight.
virtual QString flightMode(uint8_t base_mode, uint32_t custom_mode) const
virtual QString vehicleImageOpaque(const Vehicle *) const
Return the resource file which contains the vehicle icon used in the flight view when the view is dar...
virtual void startTakeoff(Vehicle *vehicle) const
Command the vehicle to start a takeoff.
virtual QList< MAV_CMD > supportedMissionCommands(QGCMAVLink::VehicleClass_t) const
List of supported mission commands. Empty list for all commands supported.
virtual void pauseVehicle(Vehicle *vehicle) const
virtual QVariant expandedToolbarIndicatorSource(const Vehicle *, const QString &) const
virtual bool mulirotorSpeedLimitsAvailable(Vehicle *) const
int versionCompare(const Vehicle *vehicle, const QString &compare) const
virtual bool hasGripper(const Vehicle *) const
virtual QString landFlightMode() const
Returns the flight mode for Land.
virtual QString gotoFlightMode() const
Returns the flight mode which the vehicle will be in if it is performing a goto location.
virtual Autotune * createAutotune(Vehicle *vehicle) const
Creates Autotune object.
virtual QStringList flightModes(Vehicle *) const
virtual AutoPilotPlugin * autopilotPlugin(Vehicle *vehicle) const
virtual void guidedModeRTL(Vehicle *vehicle, bool smartRTL) const
Command vehicle to return to launch.
virtual QString autoDisarmParameter(Vehicle *) const
virtual QString stabilizedFlightMode() const
Returns the flight mode for Stabilized.
virtual QString brandImageIndoor(const Vehicle *) const
Return the resource file which contains the brand image for the vehicle for Indoor theme.
virtual void adjustOutgoingMavlinkMessageThreadSafe(Vehicle *, LinkInterface *, mavlink_message_t *)
void toolIndicatorsChanged()
virtual void setGuidedMode(Vehicle *vehicle, bool guidedMode) const
Set guided flight mode.
virtual QString takeOffFlightMode() const
Returns the flight mode for TakeOff.
virtual void guidedModeGotoLocation(Vehicle *vehicle, const QGeoCoordinate &gotoCoord, double forwardFlightLoiterRadius=0.0) const
Command vehicle to move to specified location (altitude is included and relative)
virtual QString followFlightMode() const
Returns the flight mode which the vehicle will be for follow me.
virtual uint32_t highLatencyCustomModeTo32Bits(uint16_t hlCustomMode) const
Convert from HIGH_LATENCY2.custom_mode value to correct 32 bit value.
virtual QGCCameraManager * createCameraManager(Vehicle *vehicle) const
Creates vehicle camera manager.
virtual QMap< QString, FactGroup * > * factGroups()
Returns a pointer to a dictionary of firmware-specific FactGroups.
virtual void initializeVehicle(Vehicle *)
Called when Vehicle is first created to perform any firmware specific setup.
virtual bool MAV_CMD_DO_SET_MODE_is_supported() const
returns true if this flight stack supports MAV_CMD_DO_SET_MODE
virtual QString rtlFlightMode() const
Returns the flight mode for RTL.
virtual void guidedModeLand(Vehicle *vehicle) const
Command vehicle to land at current location.
virtual bool sendHomePositionToVehicle() const
virtual bool isGuidedMode(const Vehicle *) const
Returns whether the vehicle is in guided mode or not.
virtual double minimumTakeoffAltitudeMeters(Vehicle *) const
virtual QString getHobbsMeter(Vehicle *vehicle) const
gets hobbs meter from autopilot. This should be reimplmeented for each firmware
Fact *updateHomePosition READ updateHomePosition CONSTANT Fact * updateHomePosition()
Fact *maxGoToLocationDistance READ maxGoToLocationDistance CONSTANT Fact * maxGoToLocationDistance()
Fact *requestControlTimeout READ requestControlTimeout CONSTANT Fact * requestControlTimeout()
void error(int errorCode, const QString &errorMsg)
void loadComplete(void)
void setModeGroups(int takeoffModeGroup, int missionModeGroup)
uint32_t flowImageIndex() const
void flowImageIndexChanged(uint32_t index)
void imageReady(const QImage &image)
The link interface defines the interface for all links used to communicate with the ground station ap...
virtual bool isConnected() const =0
void writeBytesThreadSafe(const char *bytes, int length)
static int getComponentId()
Get the component id of this application.
void mavlinkMessageStatus(int sysid, uint64_t totalSent, uint64_t totalReceived, uint64_t totalLoss, float lossPercent)
void messageReceived(LinkInterface *link, const mavlink_message_t &message)
Message received and directly copied via signal.
static MAVLinkProtocol * instance()
int getSystemId() const
Get the system id of this application.
static MissionCommandTree * instance()
QString rawName(MAV_CMD command) const
Returns the raw name for the specified command.
QString friendlyName(MAV_CMD command) const
Returns the friendly name for the specified command.
int currentIndex(void) const
Current mission item as reported by MISSION_CURRENT.
void parameterReadyVehicleAvailableChanged(bool parameterReadyVehicleAvailable)
void activeVehicleChanged(Vehicle *activeVehicle)
Fact * getParameter(int componentId, const QString &paramName)
void parametersReadyChanged(bool parametersReady)
static constexpr int defaultComponentId
void loadProgressChanged(float value)
void currentIndexChanged(int currentIndex)
void sendComplete(bool error)
void newMissionItemsAvailable(bool removeAllRequested)
const QList< MissionItem * > & missionItems(void)
Definition PlanManager.h:25
void error(int errorCode, const QString &errorMsg)
static void sendPlanToVehicle(Vehicle *vehicle, const QString &filename)
void showAdvancedUIChanged(bool showAdvancedUI)
static QGCPressure * instance()
bool clockwiseRotation(void) const
void setClockwiseRotation(bool clockwiseRotation)
void setShowRotation(bool showRotation)
Fact * radius(void)
void setCenter(QGeoCoordinate newCenter)
QGeoCoordinate center(void) const
void gcsPositionChanged(QGeoCoordinate gcsPosition)
This is a QGeoCoordinate within a QObject such that it can be used on a QmlObjectListModel.
void progressUpdate(float progress)
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
void loadComplete(void)
void error(int errorCode, const QString &errorMsg)
void mavlinkMessageReceived(mavlink_message_t &message)
Provides access to all app settings.
void modesUpdated()
void availableModesMonitorReceived(uint8_t seq)
bool messageTypeNone() const
bool messageTypeError() const
void handleHTMLEscapedTextMessage(MAV_COMPONENT componentid, MAV_SEVERITY severity, const QString &text, const QString &description)
bool messageTypeNormal() const
void newErrorMessage(QString message)
void mavlinkMessageReceived(const mavlink_message_t &message)
void messageCountChanged(uint32_t newCount)
void textMessageReceived(MAV_COMPONENT componentid, MAV_SEVERITY severity, QString text, QString description)
uint32_t messageCount() const
void newFormattedMessage(QString message)
QString formattedMessages() const
void messageTypeChanged()
bool messageTypeWarning() const
NOTE: TerrainAtCoordinateQuery is not thread safe. All instances/calls to ElevationProvider must be o...
void terrainDataReceived(bool success, const QList< double > &heights)
void requestData(const QList< QGeoCoordinate > &coordinates)
bool mavlinkMessageReceived(const mavlink_message_t &message)
WeakLinkInterfacePtr primaryLink() const
void mavlinkMessageReceived(LinkInterface *link, const mavlink_message_t &message)
bool containsLink(LinkInterface *link)
void update(mavlink_obstacle_distance_t *message)
bool guidedMode() const
bool changeHeading() const
bool orbitMode() const
bool roiMode() const
bool pauseVehicle() const
void triggerSimpleCamera(void)
Trigger camera using MAV_CMD_DO_DIGICAM_CONTROL command.
Definition Vehicle.cc:4048
bool sub() const
Definition Vehicle.cc:1854
VehicleGPSAggregateFactGroup _gpsAggregateFactGroup
Definition Vehicle.h:1223
bool isInitialConnectComplete() const
Definition Vehicle.cc:3707
void guidedModeChangeAltitude(double altitudeChange, bool pauseVehicle)
Definition Vehicle.cc:2013
void _setLanding(bool landing)
Definition Vehicle.cc:1918
Vehicle(LinkInterface *link, int vehicleId, int defaultComponentId, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, QObject *parent=nullptr)
Definition Vehicle.cc:74
void autoDisarmChanged()
QGeoCoordinate _roiCoordinate
Definition Vehicle.h:1261
bool px4Firmware() const
Definition Vehicle.h:495
void motorInterlock(bool enable)
Command vehicle to Enable/Disable Motor Interlock.
Definition Vehicle.cc:4395
void virtualTabletJoystickValue(double roll, double pitch, double yaw, double thrust)
Definition Vehicle.cc:1818
int32_t getMessageRate(uint8_t compId, uint16_t msgId)
Definition Vehicle.cc:4291
TerrainProtocolHandler * _terrainProtocolHandler
Definition Vehicle.h:1243
void firmwareTypeChanged()
QGCMAVLink::VehicleClass_t vehicleClass(void) const
Definition Vehicle.h:429
void setInitialGCSPressure(qreal pressure)
Definition Vehicle.h:417
void flashBootloader()
Definition Vehicle.cc:4383
void sensorsPresentBitsChanged(int sensorsPresentBits)
void defaultHoverSpeedChanged(double hoverSpeed)
friend class InitialConnectStateMachine
Definition Vehicle.h:97
const QString _vibrationFactGroupName
Definition Vehicle.h:1206
bool _multirotor_speed_limits_available
Definition Vehicle.h:1196
void gcsControlStatusChanged()
void defaultCruiseSpeedChanged(double cruiseSpeed)
const QString _gpsFactGroupName
Definition Vehicle.h:1202
VehicleVibrationFactGroup _vibrationFactGroup
Definition Vehicle.h:1225
void haveMRSpeedLimChanged()
void sendMavCommandWithLambdaFallback(std::function< void()> lambda, int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
Definition Vehicle.cc:2403
void sendMavCommand(int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
Definition Vehicle.cc:2309
const QString _hygrometerFactGroupName
Definition Vehicle.h:1215
void messagesLostChanged()
void rebootVehicle()
Reboot vehicle.
Definition Vehicle.cc:3209
const QString _localPositionFactGroupName
Definition Vehicle.h:1211
void vtolInFwdFlightChanged(bool vtolInFwdFlight)
QString missionFlightMode() const
Definition Vehicle.cc:3436
void readyToFlyAvailableChanged(bool readyToFlyAvailable)
void forceInitialPlanRequestComplete()
Definition Vehicle.cc:3610
void isROIEnabledChanged()
void guidedModeOrbit(const QGeoCoordinate &centerCoord, double radius, double amslAltitude)
Definition Vehicle.cc:2042
const QString _terrainFactGroupName
Definition Vehicle.h:1214
Actuators * _actuators
Definition Vehicle.h:1251
const QString _temperatureFactGroupName
Definition Vehicle.h:1207
void _offlineVehicleTypeSettingChanged(QVariant varVehicleType)
Definition Vehicle.cc:433
void guidedModeTakeoff(double altitudeRelative)
Command vehicle to takeoff from current location.
Definition Vehicle.cc:1949
QString flightMode() const
Definition Vehicle.cc:1549
static QString mavCmdResultFailureCodeToString(MavCmdResultFailureCode_t failureCode)
Definition Vehicle.cc:4364
QString stabilizedFlightMode() const
Definition Vehicle.cc:3476
void requestOperatorControlReceived(int sysIdRequestingControl, int allowTakeover, int requestTimeoutSecs)
QVariant expandedToolbarIndicatorSource(const QString &indicatorName)
Definition Vehicle.cc:4340
void setActuatorsMetadata(uint8_t compid, const QString &metadataJsonFileName)
Definition Vehicle.cc:1293
bool vtol() const
Definition Vehicle.cc:1869
void setSoloFirmware(bool soloFirmware)
Definition Vehicle.cc:3328
void requestDataStream(MAV_DATA_STREAM stream, uint16_t rate, bool sendMultiple=true)
Definition Vehicle.cc:1610
QGeoCoordinate homePosition()
Definition Vehicle.cc:1515
const QString _estimatorStatusFactGroupName
Definition Vehicle.h:1213
QString vehicleImageOutline() const
Definition Vehicle.cc:3489
MissionManager * _missionManager
Definition Vehicle.h:1245
void textMessageReceived(int sysid, int componentid, int severity, QString text, QString description)
void initialPlanRequestCompleteChanged(bool initialPlanRequestComplete)
double minimumEquivalentAirspeed()
Definition Vehicle.cc:1975
QString brandImageIndoor() const
Definition Vehicle.cc:3341
const QString _clockFactGroupName
Definition Vehicle.h:1208
TerrainFactGroup _terrainFactGroup
Definition Vehicle.h:1237
bool rover() const
Definition Vehicle.cc:1849
bool multiRotor() const
Definition Vehicle.cc:1864
bool flying() const
Definition Vehicle.h:501
void stopGuidedModeROI()
Definition Vehicle.cc:2166
void firmwareCustomVersionChanged()
QString pauseFlightMode() const
Definition Vehicle.cc:3441
QString prearmError() const
Definition Vehicle.h:473
void guidedModeROI(const QGeoCoordinate &centerCoord)
Definition Vehicle.cc:2074
float latitude()
Definition Vehicle.h:492
const QString _efiFactGroupName
Definition Vehicle.h:1217
void inFwdFlightChanged()
void flyingChanged(bool flying)
void setFirmwarePluginInstanceData(FirmwarePluginInstanceData *firmwarePluginInstanceData)
Definition Vehicle.cc:3430
TerrainAtCoordinateQuery * _currentDoSetHomeTerrainAtCoordinateQuery
Definition Vehicle.h:1256
void remoteControlRSSIChanged(uint8_t rssi)
Remote control RSSI changed (0% - 100%)
bool hasGripper() const
Definition Vehicle.cc:1980
void sendMessageMultiple(mavlink_message_t message)
Definition Vehicle.cc:1668
void capabilityBitsChanged(uint64_t capabilityBits)
void _setHomePosition(QGeoCoordinate &homeCoord)
Definition Vehicle.cc:1079
void hobbsMeterChanged()
uint64_t capabilityBits() const
Definition Vehicle.h:738
void abortLanding(double climbOutAltitude)
Command vehicle to abort landing.
Definition Vehicle.cc:2219
VehicleTemperatureFactGroup _temperatureFactGroup
Definition Vehicle.h:1226
void setGuidedMode(bool guidedMode)
Definition Vehicle.cc:2233
void readyToFlyChanged(bool readyToFy)
QString firmwareVersionTypeString() const
Definition Vehicle.cc:3182
QString landFlightMode() const
Definition Vehicle.cc:3456
static void showCommandAckError(const mavlink_command_ack_t &ack)
Definition Vehicle.cc:2731
void newFormattedMessage(QString formattedMessage)
MAV_TYPE vehicleType() const
Definition Vehicle.h:428
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:580
uint32_t customMode() const
Definition Vehicle.h:507
void cameraManagerChanged()
void sendParamMapRC(const QString &paramName, double scale, double centerValue, int tuningID, double minValue, double maxValue)
Sends PARAM_MAP_RC message to vehicle.
Definition Vehicle.cc:3930
double maximumHorizontalSpeedMultirotor()
Definition Vehicle.cc:1963
void armedChanged(bool armed)
void firmwareVersionChanged()
void logEntry(uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t last_log_num)
void sendControlRequestAllowedChanged(bool sendControlRequestAllowed)
void hasGripperChanged()
FirmwarePlugin * firmwarePlugin()
Provides access to the Firmware Plugin for this Vehicle.
Definition Vehicle.h:444
void sendMavCommandInt(int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
Definition Vehicle.cc:2350
double minimumTakeoffAltitudeMeters()
Definition Vehicle.cc:1958
InitialConnectStateMachine * _initialConnectStateMachine
Definition Vehicle.h:1250
void sensorsParametersResetAck(bool success)
void sendMavCommandDelayed(int compId, MAV_CMD command, bool showError, int milliseconds, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
Definition Vehicle.cc:2320
bool guidedMode() const
Definition Vehicle.cc:2228
MavCmdResultFailureCode_t
Definition Vehicle.h:618
@ MavCmdResultFailureNoResponseToCommand
No response from vehicle to command.
Definition Vehicle.h:620
@ MavCmdResultCommandResultOnly
commandResult specifies full success/fail info
Definition Vehicle.h:619
@ MavCmdResultFailureDuplicateCommand
Unable to send command since duplicate is already being waited on for response.
Definition Vehicle.h:621
void telemetryLNoiseChanged(int value)
const QString _distanceSensorFactGroupName
Definition Vehicle.h:1210
double maximumEquivalentAirspeed()
Definition Vehicle.cc:1969
PIDTuningTelemetryMode
Definition Vehicle.h:371
@ ModeDisabled
Definition Vehicle.h:372
@ ModeAltitudeAndAirspeed
Definition Vehicle.h:375
@ ModeVelocityAndPosition
Definition Vehicle.h:374
@ ModeRateAndAttitude
Definition Vehicle.h:373
void setPrearmError(const QString &prearmError)
Definition Vehicle.cc:3151
bool landing() const
Definition Vehicle.h:502
~Vehicle()
Definition Vehicle.cc:351
void setFirmwareCustomVersion(int majorVersion, int minorVersion, int patchVersion)
Definition Vehicle.cc:3174
void startTimerRevertAllowTakeover()
Definition Vehicle.cc:4114
const QString _generatorFactGroupName
Definition Vehicle.h:1216
QElapsedTimer _altitudeAboveTerrQueryTimer
Definition Vehicle.h:1264
void messagesSentChanged()
VehicleDistanceSensorFactGroup _distanceSensorFactGroup
Definition Vehicle.h:1229
void sendPlan(QString planFile)
Definition Vehicle.cc:3616
bool vtolInFwdFlight() const
Definition Vehicle.h:505
void startMission()
Definition Vehicle.cc:1991
bool isMavCommandPending(int targetCompId, MAV_CMD command)
isMavCommandPending Query whether the specified MAV_CMD is in queue to be sent or has already been se...
Definition Vehicle.cc:2460
void capabilitiesKnownChanged(bool capabilitiesKnown)
void sendGripperAction(QGCMAVLink::GripperActions gripperOption)
Definition Vehicle.cc:4057
void requestOperatorControl(bool allowOverride, int requestTimeoutSecs=0)
Definition Vehicle.cc:4130
QGeoCoordinate _altitudeAboveTerrLastCoord
Definition Vehicle.h:1267
bool soloFirmware() const
Definition Vehicle.h:708
void coordinateChanged(QGeoCoordinate coordinate)
QString vehicleImageOpaque() const
Definition Vehicle.cc:3481
float _altitudeTuningOffset
Definition Vehicle.h:1193
QString brandImageOutdoor() const
Definition Vehicle.cc:3346
void setPIDTuningTelemetryMode(PIDTuningTelemetryMode mode)
Definition Vehicle.cc:3656
void mavlinkLogData(Vehicle *vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t first_message, QByteArray data, bool acked)
void emergencyStop()
Command vehicle to kill all motors no matter what state.
Definition Vehicle.cc:2244
void updateFlightDistance(double distance)
Definition Vehicle.cc:3925
int id() const
Definition Vehicle.h:425
const QString _gpsAggregateFactGroupName
Definition Vehicle.h:1204
void pairRX(int rxType, int rxSubType)
Definition Vehicle.cc:4090
const QString _setpointFactGroupName
Definition Vehicle.h:1209
void setCurrentMissionSequence(int seq)
Alter the current mission item on the vehicle.
Definition Vehicle.cc:2274
void guidedModeGotoLocation(const QGeoCoordinate &gotoCoord, double forwardFlightLoiterRadius=0.0f)
Command vehicle to move to specified location (altitude is ignored)
Definition Vehicle.cc:1996
void toolIndicatorsChanged()
void sensorsUnhealthyBitsChanged(int sensorsUnhealthyBits)
void landingChanged(bool landing)
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
Definition Vehicle.cc:1470
VehicleLocalPositionSetpointFactGroup _localPositionSetpointFactGroup
Definition Vehicle.h:1231
TerrainAtCoordinateQuery * _roiTerrainAtCoordinateQuery
Definition Vehicle.h:1260
bool inFwdFlight() const
Definition Vehicle.cc:2238
const QString _rpmFactGroupName
Definition Vehicle.h:1218
static const MAV_AUTOPILOT MAV_AUTOPILOT_TRACK
Definition Vehicle.h:119
VehicleGPS2FactGroup _gps2FactGroup
Definition Vehicle.h:1222
void startTakeoff()
Definition Vehicle.cc:1985
VehicleEstimatorStatusFactGroup _estimatorStatusFactGroup
Definition Vehicle.h:1232
void setFlightMode(const QString &flightMode)
Definition Vehicle.cc:1559
void sendCommand(int compId, int command, bool showError, double param1=0.0, double param2=0.0, double param3=0.0, double param4=0.0, double param5=0.0, double param6=0.0, double param7=0.0)
Same as sendMavCommand but available from Qml.
Definition Vehicle.cc:2325
void loadProgressChanged(float value)
QString smartRTLFlightMode() const
Definition Vehicle.cc:3451
QString followFlightMode() const
Definition Vehicle.cc:3466
bool _fixed_wing_airspeed_limits_available
Definition Vehicle.h:1197
void motorTest(int motor, int percent, int timeoutSecs, bool showError)
Definition Vehicle.cc:3336
void mavlinkMessageReceived(const mavlink_message_t &message)
void setMessageRate(uint8_t compId, uint16_t msgId, int32_t rate)
Definition Vehicle.cc:4319
void _sendMavCommandFromList(int index)
Definition Vehicle.cc:2620
VehicleRPMFactGroup _rpmFactGroup
Definition Vehicle.h:1236
const QVariantList & toolIndicators()
Definition Vehicle.cc:3497
void _offlineFirmwareTypeSettingChanged(QVariant varFirmwareType)
Definition Vehicle.cc:420
void guidedModeChangeEquivalentAirspeedMetersSecond(double airspeed)
Definition Vehicle.cc:2033
float _altitudeAboveTerrLastRelAlt
Definition Vehicle.h:1268
void guidedModeChanged(bool guidedMode)
void setEventsMetadata(uint8_t compid, const QString &metadataJsonFileName)
Definition Vehicle.cc:1268
int _findMavCommandListEntryIndex(int targetCompId, MAV_CMD command)
Definition Vehicle.cc:2467
bool autoDisarm()
Definition Vehicle.cc:3517
QString motorDetectionFlightMode() const
Definition Vehicle.cc:3471
RequestMessageResultHandlerFailureCode_t
Definition Vehicle.h:668
@ RequestMessageFailureMessageNotReceived
Definition Vehicle.h:672
@ RequestMessageNoFailure
Definition Vehicle.h:669
@ RequestMessageFailureCommandNotAcked
Definition Vehicle.h:671
@ RequestMessageFailureCommandError
Definition Vehicle.h:670
@ RequestMessageFailureDuplicate
Exact duplicate request already active or queued for this component/message id.
Definition Vehicle.h:673
bool coaxialMotors()
Definition Vehicle.cc:1500
void orbitActiveChanged(bool orbitActive)
void allSensorsHealthyChanged(bool allSensorsHealthy)
void guidedModeRTL(bool smartRTL)
Command vehicle to return to launch.
Definition Vehicle.cc:1931
bool xConfigMotors()
Definition Vehicle.cc:1505
VehicleSetpointFactGroup _setpointFactGroup
Definition Vehicle.h:1228
void trackFirmwareVehicleTypeChanges(void)
Definition Vehicle.cc:214
void landingGearDeploy()
Command vichecle to deploy landing gear.
Definition Vehicle.cc:2254
static constexpr int kTestMavCommandAckTimeoutMs
Definition Vehicle.h:1177
void armedPositionChanged()
void rcRSSIChanged(int rcRSSI)
int motorCount()
Definition Vehicle.cc:1491
void stopMavlinkLog()
Definition Vehicle.cc:3376
static QString requestMessageResultHandlerFailureCodeToString(RequestMessageResultHandlerFailureCode_t failureCode)
Definition Vehicle.cc:4345
RemoteIDManager * _remoteIDManager
Definition Vehicle.h:1252
void doSetHome(const QGeoCoordinate &coord)
Set home from flight map coordinate.
Definition Vehicle.cc:3770
void stopUAVCANBusConfig(void)
Definition Vehicle.cc:3320
bool _commandCanBeDuplicated(MAV_CMD command)
Definition Vehicle.cc:2522
EscStatusFactGroupListModel _escStatusFactGroupListModel
Definition Vehicle.h:1241
void soloFirmwareChanged(bool soloFirmware)
uint32_t flowImageIndex() const
Definition Vehicle.cc:4531
int compId() const
Definition Vehicle.h:426
void homePositionChanged(const QGeoCoordinate &homePosition)
QMap< uint8_t, uint8_t > _lowestBatteryChargeStateAnnouncedMap
Definition Vehicle.h:1191
VehicleHygrometerFactGroup _hygrometerFactGroup
Definition Vehicle.h:1233
void pauseVehicle()
Definition Vehicle.cc:2210
void stopTrackingFirmwareVehicleTypeChanges(void)
Definition Vehicle.cc:223
VehicleClockFactGroup _clockFactGroup
Definition Vehicle.h:1227
void flightModesChanged()
const QString _gps2FactGroupName
Definition Vehicle.h:1203
bool apmFirmware() const
Definition Vehicle.h:496
void flightModeChanged(const QString &flightMode)
void telemetryLRSSIChanged(int value)
void flowImageIndexChanged()
void roiCoordChanged(const QGeoCoordinate &centerCoord)
void clearAllParamMapRC(void)
Clears all PARAM_MAP_RC settings from vehicle.
Definition Vehicle.cc:3964
void messageTypeChanged()
void setEstimatorOrigin(const QGeoCoordinate &centerCoord)
Definition Vehicle.cc:4067
void setVtolInFwdFlight(bool vtolInFwdFlight)
Definition Vehicle.cc:3360
void guidedModeChangeGroundSpeedMetersSecond(double groundspeed)
Definition Vehicle.cc:2023
int defaultComponentId() const
Definition Vehicle.h:711
void setInitialGCSTemperature(qreal temperature)
Definition Vehicle.h:418
void mavlinkStatusChanged()
MAVLinkLogManager * mavlinkLogManager() const
Definition Vehicle.cc:4546
void sendJoystickDataThreadSafe(float roll, float pitch, float yaw, float thrust, quint16 buttons, quint16 buttons2, float pitchExtension, float rollExtension, float aux1, float aux2, float aux3, float aux4, float aux5, float aux6)
Definition Vehicle.cc:3990
FTPManager * _ftpManager
Definition Vehicle.h:1249
GeoFenceManager * _geoFenceManager
Definition Vehicle.h:1246
float longitude()
Definition Vehicle.h:493
bool flightModeSetAvailable()
Definition Vehicle.cc:1538
ParameterManager * parameterManager()
Definition Vehicle.h:578
void sensorsHealthBitsChanged(int sensorsHealthBits)
void _setFlying(bool flying)
Definition Vehicle.cc:1910
VehicleLocalPositionFactGroup _localPositionFactGroup
Definition Vehicle.h:1230
void guidedModeChangeHeading(const QGeoCoordinate &headingCoord)
Definition Vehicle.cc:2200
QGeoCoordinate _doSetHomeCoordinate
Definition Vehicle.h:1257
QString takeControlFlightMode() const
Definition Vehicle.cc:3461
void messagesReceivedChanged()
const QString _localPositionSetpointFactGroupName
Definition Vehicle.h:1212
void setOfflineEditingDefaultComponentId(int defaultComponentId)
Sets the default component id for an offline editing vehicle.
Definition Vehicle.cc:3351
TerrainAtCoordinateQuery * _altitudeAboveTerrTerrainAtCoordinateQuery
Definition Vehicle.h:1265
void setFirmwareVersion(int majorVersion, int minorVersion, int patchVersion, FIRMWARE_VERSION_TYPE versionType=FIRMWARE_VERSION_TYPE_OFFICIAL)
Definition Vehicle.cc:3165
void sendSetupSigning()
Definition Vehicle.cc:4490
QString rtlFlightMode() const
Definition Vehicle.cc:3446
VehicleGeneratorFactGroup _generatorFactGroup
Definition Vehicle.h:1234
VehicleEFIFactGroup _efiFactGroup
Definition Vehicle.h:1235
bool fixedWing() const
Definition Vehicle.cc:1844
void requestMessage(RequestMessageResultHandler resultHandler, void *resultHandlerData, int compId, int messageId, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f)
Definition Vehicle.cc:3099
QGeoCoordinate coordinate()
Definition Vehicle.h:412
void startCalibration(QGCMAVLink::CalibrationType calType)
Definition Vehicle.cc:3218
QString firmwareTypeString() const
Definition Vehicle.cc:451
void stopCalibration(bool showError)
Definition Vehicle.cc:3298
void messageCountChanged()
void rcChannelsChanged(QVector< int > channelValues)
BatteryFactGroupListModel _batteryFactGroupListModel
Definition Vehicle.h:1240
void telemetryTXBufferChanged(unsigned int value)
friend class VehicleLinkManager
Definition Vehicle.h:98
void haveFWSpeedLimChanged()
RallyPointManager * _rallyPointManager
Definition Vehicle.h:1247
void startUAVCANBusConfig(void)
Definition Vehicle.cc:3312
QString gotoFlightMode() const
Definition Vehicle.cc:1926
void sendMavCommandIntWithHandler(const MavCmdAckHandlerInfo_t *ackHandlerInfo, int compId, MAV_CMD command, MAV_FRAME frame, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, double param5=0.0f, double param6=0.0f, float param7=0.0f)
Definition Vehicle.cc:2361
void telemetryRRSSIChanged(int value)
bool armed() const
Definition Vehicle.h:449
void landingGearRetract()
Command vichecle to retract landing gear.
Definition Vehicle.cc:2264
VehicleSupports * supports()
Definition Vehicle.h:405
void telemetryFixedChanged(unsigned int value)
const QString _windFactGroupName
Definition Vehicle.h:1205
void mavCommandResult(int vehicleId, int targetComponent, int command, int ackResult, int failureCode)
void calibrationEventReceived(int uasid, int componentid, int severity, QSharedPointer< events::parser::ParsedEvent > event)
void sensorsEnabledBitsChanged(int sensorsEnabledBits)
class FirmwarePluginInstanceData * firmwarePluginInstanceData()
Definition Vehicle.h:726
int versionCompare(const QString &compare) const
Used to check if running current version is equal or higher than the one being compared.
Definition Vehicle.cc:3646
void forceArm()
Definition Vehicle.cc:1529
void requiresGpsFixChanged()
void setArmed(bool armed, bool showError)
Definition Vehicle.cc:1520
void closeVehicle(void)
Removes the vehicle from the system.
Definition Vehicle.h:390
bool _sendMavCommandShouldRetry(MAV_CMD command)
Definition Vehicle.cc:2491
QStringList flightModes()
Definition Vehicle.cc:1543
void mavlinkMsgIntervalsChanged(uint8_t compid, uint16_t msgId, int32_t rate)
void mavlinkSerialControl(uint8_t device, uint8_t flags, uint16_t timeout, uint32_t baudrate, QByteArray data)
QString vehicleClassInternalName() const
Definition Vehicle.cc:1879
VehicleLinkManager * _vehicleLinkManager
Definition Vehicle.h:1248
VehicleWindFactGroup _windFactGroup
Definition Vehicle.h:1224
QString hobbsMeter()
Definition Vehicle.cc:3621
VehicleGPSFactGroup _gpsFactGroup
Definition Vehicle.h:1221
void prearmErrorChanged(const QString &prearmError)
void startMavlinkLog()
Definition Vehicle.cc:3371
void vehicleTypeChanged()
void guidedModeLand()
Command vehicle to land at current location.
Definition Vehicle.cc:1940
void sendMavCommandWithHandler(const MavCmdAckHandlerInfo_t *ackHandlerInfo, int compId, MAV_CMD command, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
Sends the command and calls the callback with the result.
Definition Vehicle.cc:2339
bool airship() const
Definition Vehicle.cc:1839
void _sendMavCommandWorker(bool commandInt, bool showError, const MavCmdAckHandlerInfo_t *ackHandlerInfo, int compId, MAV_CMD command, MAV_FRAME frame, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
Definition Vehicle.cc:2537
StandardModes * _standardModes
Definition Vehicle.h:1253
QString vehicleUIDStr()
Definition Vehicle.cc:907
bool spacecraft() const
Definition Vehicle.cc:1859
void telemetryRNoiseChanged(int value)
friend class GimbalController
Definition Vehicle.h:106
QString vehicleTypeString() const
Definition Vehicle.cc:1874
void logData(uint32_t ofs, uint16_t id, uint8_t count, const uint8_t *data)
void telemetryRXErrorsChanged(unsigned int value)
static constexpr const char * videoSourceUDPH264
Fact *streamEnabled READ streamEnabled CONSTANT Fact * streamEnabled()
static constexpr const char * videoDisabled
Fact *videoSource READ videoSource CONSTANT Fact * videoSource()
Fact *lowLatencyMode READ lowLatencyMode CONSTANT Fact * lowLatencyMode()
@ MOTOR_INTERLOCK
Definition APM.h:35
void createSetupSigning(mavlink_channel_t channel, mavlink_system_t target_system, mavlink_setup_signing_t &setup_signing)
bool fuzzyCompare(double value1, double value2)
Returns true if the two values are equal or close. Correctly handles 0 and NaN values.
Definition QGC.cc:106
MavCmdResultHandler resultHandler
Definition Vehicle.h:639
void * resultHandlerData
‍nullptr for no handler
Definition Vehicle.h:640