8 changed files with 307 additions and 2 deletions
@ -0,0 +1,179 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2022 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#include "Vehicle.h" |
||||||
|
#include "StandardModes.h" |
||||||
|
|
||||||
|
QGC_LOGGING_CATEGORY(StandardModesLog, "StandardModesLog") |
||||||
|
|
||||||
|
|
||||||
|
static void requestMessageResultHandler(void* resultHandlerData, MAV_RESULT result, |
||||||
|
Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t &message) |
||||||
|
{ |
||||||
|
StandardModes* standardModes = static_cast<StandardModes*>(resultHandlerData); |
||||||
|
standardModes->gotMessage(result, message); |
||||||
|
} |
||||||
|
|
||||||
|
StandardModes::StandardModes(QObject *parent, Vehicle *vehicle) |
||||||
|
: QObject(parent), _vehicle(vehicle) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void StandardModes::gotMessage(MAV_RESULT result, const mavlink_message_t &message) |
||||||
|
{ |
||||||
|
_requestActive = false; |
||||||
|
if (_wantReset) { |
||||||
|
_wantReset = false; |
||||||
|
request(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (result == MAV_RESULT_ACCEPTED) { |
||||||
|
mavlink_available_modes_t availableModes; |
||||||
|
mavlink_msg_available_modes_decode(&message, &availableModes); |
||||||
|
bool cannotBeSet = availableModes.properties & MAV_MODE_PROPERTY_NOT_USER_SELECTABLE; |
||||||
|
bool advanced = availableModes.properties & MAV_MODE_PROPERTY_ADVANCED; |
||||||
|
availableModes.mode_name[sizeof(availableModes.mode_name)-1] = '\0'; |
||||||
|
QString name = availableModes.mode_name; |
||||||
|
switch (availableModes.standard_mode) { |
||||||
|
case MAV_STANDARD_MODE_POSITION_HOLD: |
||||||
|
name = "Position"; |
||||||
|
break; |
||||||
|
case MAV_STANDARD_MODE_ORBIT: |
||||||
|
name = "Orbit"; |
||||||
|
break; |
||||||
|
case MAV_STANDARD_MODE_CRUISE: |
||||||
|
name = "Cruise"; |
||||||
|
break; |
||||||
|
case MAV_STANDARD_MODE_ALTITUDE_HOLD: |
||||||
|
name = "Altitude"; |
||||||
|
break; |
||||||
|
case MAV_STANDARD_MODE_RETURN_HOME: |
||||||
|
name = "Return"; |
||||||
|
break; |
||||||
|
case MAV_STANDARD_MODE_SAFE_RECOVERY: |
||||||
|
name = "Safe Recovery"; |
||||||
|
break; |
||||||
|
case MAV_STANDARD_MODE_MISSION: |
||||||
|
name = "Mission"; |
||||||
|
break; |
||||||
|
case MAV_STANDARD_MODE_LAND: |
||||||
|
name = "Land"; |
||||||
|
break; |
||||||
|
case MAV_STANDARD_MODE_TAKEOFF: |
||||||
|
name = "Takeoff"; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (name == "Takeoff" || name == "VTOL Takeoff" || name == "Orbit" || name == "Land" || name == "Return") { // These are exposed in the UI as separate buttons
|
||||||
|
cannotBeSet = true; |
||||||
|
} |
||||||
|
|
||||||
|
qCDebug(StandardModesLog) << "Got mode:" << name << ", idx:" << availableModes.mode_index << ", custom_mode" << availableModes.custom_mode; |
||||||
|
|
||||||
|
_nextModes[availableModes.custom_mode] = Mode{name, availableModes.standard_mode, advanced, cannotBeSet}; |
||||||
|
|
||||||
|
if (availableModes.mode_index >= availableModes.number_modes) { // We are done
|
||||||
|
qCDebug(StandardModesLog) << "Completed, num modes:" << _nextModes.size(); |
||||||
|
_modes = _nextModes; |
||||||
|
ensureUniqueModeNames(); |
||||||
|
_hasModes = true; |
||||||
|
emit modesUpdated(); |
||||||
|
emit requestCompleted(); |
||||||
|
|
||||||
|
} else { |
||||||
|
requestMode(availableModes.mode_index + 1); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
qCDebug(StandardModesLog) << "Failed to retrieve available modes" << result; |
||||||
|
emit requestCompleted(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void StandardModes::ensureUniqueModeNames() |
||||||
|
{ |
||||||
|
// Ensure mode names are unique. This should generally already be the case, but e.g. during development when
|
||||||
|
// restarting dynamic modes, it might not be.
|
||||||
|
for (auto iter = _modes.begin(); iter != _modes.end(); ++iter) { |
||||||
|
int duplicateIdx = 0; |
||||||
|
for (auto iter2 = iter + 1; iter2 != _modes.end(); ++iter2) { |
||||||
|
if (iter.value().name == iter2.value().name) { |
||||||
|
iter2.value().name += QStringLiteral(" (%1)").arg(duplicateIdx + 1); |
||||||
|
++duplicateIdx; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void StandardModes::request() |
||||||
|
{ |
||||||
|
if (_requestActive) { |
||||||
|
// If we are in the middle of waiting for a request, wait for the response first
|
||||||
|
_wantReset = true; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
_nextModes.clear(); |
||||||
|
|
||||||
|
qCDebug(StandardModesLog) << "Requesting available modes"; |
||||||
|
// Request one at a time. This could be improved by requesting all, but we can't use Vehicle::requestMessage for that
|
||||||
|
StandardModes::requestMode(1); |
||||||
|
} |
||||||
|
|
||||||
|
void StandardModes::requestMode(int modeIndex) |
||||||
|
{ |
||||||
|
_requestActive = true; |
||||||
|
_vehicle->requestMessage( |
||||||
|
requestMessageResultHandler, |
||||||
|
this, |
||||||
|
MAV_COMP_ID_AUTOPILOT1, |
||||||
|
MAVLINK_MSG_ID_AVAILABLE_MODES, modeIndex); |
||||||
|
} |
||||||
|
|
||||||
|
void StandardModes::availableModesMonitorReceived(uint8_t seq) |
||||||
|
{ |
||||||
|
if (_lastSeq != seq) { |
||||||
|
qCDebug(StandardModesLog) << "Available modes changed, re-requesting"; |
||||||
|
_lastSeq = seq; |
||||||
|
request(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
QStringList StandardModes::flightModes() |
||||||
|
{ |
||||||
|
QStringList ret; |
||||||
|
for (const auto& mode : _modes) { |
||||||
|
if (mode.cannotBeSet) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
ret += mode.name; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
QString StandardModes::flightMode(uint32_t custom_mode) const |
||||||
|
{ |
||||||
|
auto iter = _modes.find(custom_mode); |
||||||
|
if (iter != _modes.end()) { |
||||||
|
return iter->name; |
||||||
|
} |
||||||
|
return tr("Unknown %2").arg(custom_mode); |
||||||
|
} |
||||||
|
|
||||||
|
bool StandardModes::setFlightMode(const QString &flightMode, uint32_t *custom_mode) |
||||||
|
{ |
||||||
|
for (auto iter = _modes.constBegin(); iter != _modes.constEnd(); ++iter) { |
||||||
|
if (iter->name == flightMode) { |
||||||
|
*custom_mode = iter.key(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
/****************************************************************************
|
||||||
|
* |
||||||
|
* (c) 2022 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
* |
||||||
|
* QGroundControl is licensed according to the terms in the file |
||||||
|
* COPYING.md in the root of the source code directory. |
||||||
|
* |
||||||
|
****************************************************************************/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "QGCLoggingCategory.h" |
||||||
|
#include "QGCMAVLink.h" |
||||||
|
#include <QString> |
||||||
|
#include <QMap> |
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(StandardModesLog) |
||||||
|
|
||||||
|
class Vehicle; |
||||||
|
|
||||||
|
class StandardModes : public QObject |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
|
||||||
|
public: |
||||||
|
struct Mode { |
||||||
|
QString name; |
||||||
|
uint8_t standardMode; |
||||||
|
bool advanced; |
||||||
|
bool cannotBeSet; |
||||||
|
}; |
||||||
|
|
||||||
|
StandardModes(QObject* parent, Vehicle* vehicle); |
||||||
|
|
||||||
|
void request(); |
||||||
|
|
||||||
|
void availableModesMonitorReceived(uint8_t seq); |
||||||
|
|
||||||
|
bool supported() const { return _hasModes; } |
||||||
|
|
||||||
|
QStringList flightModes(); |
||||||
|
|
||||||
|
QString flightMode(uint32_t custom_mode) const; |
||||||
|
|
||||||
|
bool setFlightMode(const QString& flightMode, uint32_t* custom_mode); |
||||||
|
|
||||||
|
void gotMessage(MAV_RESULT result, const mavlink_message_t &message); |
||||||
|
signals: |
||||||
|
void modesUpdated(); |
||||||
|
void requestCompleted(); |
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
void requestMode(int modeIndex); |
||||||
|
void ensureUniqueModeNames(); |
||||||
|
|
||||||
|
Vehicle*const _vehicle; |
||||||
|
|
||||||
|
bool _requestActive{false}; |
||||||
|
bool _wantReset{false}; |
||||||
|
QMap<uint32_t, Mode> _nextModes; ///< Modes added by current request
|
||||||
|
|
||||||
|
bool _hasModes{false}; |
||||||
|
|
||||||
|
int _lastSeq{-1}; |
||||||
|
|
||||||
|
QMap<uint32_t, Mode> _modes; ///< key is custom_mode
|
||||||
|
}; |
||||||
|
|
Loading…
Reference in new issue