Browse Source

Simple mission terrain support

QGC4.4
DonLakeFlyer 7 years ago
parent
commit
85e0c56a35
  1. 2
      src/JsonHelper.cc
  2. 2
      src/JsonHelper.h
  3. 42
      src/MissionManager/MissionController.cc
  4. 6
      src/MissionManager/MissionController.h
  5. 8
      src/MissionManager/PlanMasterController.h
  6. 308
      src/MissionManager/SimpleMissionItem.cc
  7. 51
      src/MissionManager/SimpleMissionItem.h
  8. 68
      src/MissionManager/SimpleMissionItemTest.cc
  9. 8
      src/MissionManager/SimpleMissionItemTest.h
  10. 5
      src/MissionManager/VisualMissionItem.h
  11. 16
      src/PlanView/PlanView.qml
  12. 46
      src/PlanView/SimpleItemEditor.qml
  13. 2
      src/Terrain.cc

2
src/JsonHelper.cc

@ -97,7 +97,7 @@ bool JsonHelper::validateKeyTypes(const QJsonObject& jsonObject, const QStringLi
QString valueKey = keys[i]; QString valueKey = keys[i];
if (jsonObject.contains(valueKey)) { if (jsonObject.contains(valueKey)) {
const QJsonValue& jsonValue = jsonObject[valueKey]; const QJsonValue& jsonValue = jsonObject[valueKey];
if (types[i] == QJsonValue::Null && jsonValue.type() == QJsonValue::Double) { if (jsonValue.type() == QJsonValue::Null && types[i] == QJsonValue::Double) {
// Null type signals a NaN on a double value // Null type signals a NaN on a double value
continue; continue;
} }

2
src/JsonHelper.h

@ -106,7 +106,7 @@ public:
static bool parseEnum(const QJsonObject& jsonObject, QStringList& enumStrings, QStringList& enumValues, QString& errorString, QString valueName = QString()); static bool parseEnum(const QJsonObject& jsonObject, QStringList& enumStrings, QStringList& enumValues, QString& errorString, QString valueName = QString());
/// Returns NaN if the value is null, or it not the double value /// Returns NaN if the value is null, or if not, the double value
static double possibleNaNJsonValue(const QJsonValue& value); static double possibleNaNJsonValue(const QJsonValue& value);
static const char* jsonVersionKey; static const char* jsonVersionKey;

42
src/MissionManager/MissionController.cc

@ -343,13 +343,13 @@ int MissionController::insertSimpleMissionItem(QGeoCoordinate coordinate, int i)
} }
} }
newItem->setDefaultsForCommand(); newItem->setDefaultsForCommand();
if ((MAV_CMD)newItem->command() == MAV_CMD_NAV_WAYPOINT) { if (newItem->specifiesAltitude()) {
double prevAltitude; double prevAltitude;
MAV_FRAME prevFrame; int prevAltitudeMode;
if (_findPreviousAltitude(i, &prevAltitude, &prevFrame)) { if (_findPreviousAltitude(i, &prevAltitude, &prevAltitudeMode)) {
newItem->missionItem().setFrame(prevFrame); newItem->altitude()->setRawValue(prevAltitude);
newItem->missionItem().setParam7(prevAltitude); newItem->setAltitudeMode((SimpleMissionItem::AltitudeMode)prevAltitudeMode);
} }
} }
newItem->setMissionFlightStatus(_missionFlightStatus); newItem->setMissionFlightStatus(_missionFlightStatus);
@ -373,11 +373,11 @@ int MissionController::insertROIMissionItem(QGeoCoordinate coordinate, int i)
newItem->setCoordinate(coordinate); newItem->setCoordinate(coordinate);
double prevAltitude; double prevAltitude;
MAV_FRAME prevFrame; int prevAltitudeMode;
if (_findPreviousAltitude(i, &prevAltitude, &prevFrame)) { if (_findPreviousAltitude(i, &prevAltitude, &prevAltitudeMode)) {
newItem->missionItem().setFrame(prevFrame); newItem->altitude()->setRawValue(prevAltitude);
newItem->missionItem().setParam7(prevAltitude); newItem->setAltitudeMode((SimpleMissionItem::AltitudeMode)prevAltitudeMode);
} }
_visualItems->insert(i, newItem); _visualItems->insert(i, newItem);
@ -924,6 +924,18 @@ bool MissionController::loadTextFile(QFile& file, QString& errorString)
return true; return true;
} }
bool MissionController::readyForSaveSend(void) const
{
for (int i=0; i<_visualItems->count(); i++) {
VisualMissionItem* visualItem = qobject_cast<VisualMissionItem*>(_visualItems->get(i));
if (!visualItem->readyForSave()) {
return false;
}
}
return true;
}
void MissionController::save(QJsonObject& json) void MissionController::save(QJsonObject& json)
{ {
json[JsonHelper::jsonVersionKey] = _missionFileVersion; json[JsonHelper::jsonVersionKey] = _missionFileVersion;
@ -1648,11 +1660,11 @@ void MissionController::_inProgressChanged(bool inProgress)
emit syncInProgressChanged(inProgress); emit syncInProgressChanged(inProgress);
} }
bool MissionController::_findPreviousAltitude(int newIndex, double* prevAltitude, MAV_FRAME* prevFrame) bool MissionController::_findPreviousAltitude(int newIndex, double* prevAltitude, int* prevAltitudeMode)
{ {
bool found = false; bool found = false;
double foundAltitude; double foundAltitude;
MAV_FRAME foundFrame; int foundAltitudeMode;
if (newIndex > _visualItems->count()) { if (newIndex > _visualItems->count()) {
return false; return false;
@ -1665,9 +1677,9 @@ bool MissionController::_findPreviousAltitude(int newIndex, double* prevAltitude
if (visualItem->specifiesCoordinate() && !visualItem->isStandaloneCoordinate()) { if (visualItem->specifiesCoordinate() && !visualItem->isStandaloneCoordinate()) {
if (visualItem->isSimpleItem()) { if (visualItem->isSimpleItem()) {
SimpleMissionItem* simpleItem = qobject_cast<SimpleMissionItem*>(visualItem); SimpleMissionItem* simpleItem = qobject_cast<SimpleMissionItem*>(visualItem);
if ((MAV_CMD)simpleItem->command() == MAV_CMD_NAV_WAYPOINT) { if (simpleItem->specifiesAltitude()) {
foundAltitude = simpleItem->exitCoordinate().altitude(); foundAltitude = simpleItem->altitude()->rawValue().toDouble();
foundFrame = simpleItem->missionItem().frame(); foundAltitudeMode = simpleItem->altitudeMode();
found = true; found = true;
break; break;
} }
@ -1677,7 +1689,7 @@ bool MissionController::_findPreviousAltitude(int newIndex, double* prevAltitude
if (found) { if (found) {
*prevAltitude = foundAltitude; *prevAltitude = foundAltitude;
*prevFrame = foundFrame; *prevAltitudeMode = foundAltitudeMode;
} }
return found; return found;

6
src/MissionManager/MissionController.h

@ -118,6 +118,10 @@ public:
/// @param sequenceNumber - index for new item, -1 to clear current item /// @param sequenceNumber - index for new item, -1 to clear current item
Q_INVOKABLE void setCurrentPlanViewIndex(int sequenceNumber, bool force); Q_INVOKABLE void setCurrentPlanViewIndex(int sequenceNumber, bool force);
/// Determines if the mission has all data needed to be saved or sent to the vehicle. Currently the only case where this
/// would return false is when it is still waiting on terrain data to determine correct altitudes.
bool readyForSaveSend(void) const;
/// Sends the mission items to the specified vehicle /// Sends the mission items to the specified vehicle
static void sendItemsToVehicle(Vehicle* vehicle, QmlObjectListModel* visualMissionItems); static void sendItemsToVehicle(Vehicle* vehicle, QmlObjectListModel* visualMissionItems);
@ -218,7 +222,7 @@ private:
void _setupActiveVehicle(Vehicle* activeVehicle, bool forceLoadFromVehicle); void _setupActiveVehicle(Vehicle* activeVehicle, bool forceLoadFromVehicle);
void _calcPrevWaypointValues(double homeAlt, VisualMissionItem* currentItem, VisualMissionItem* prevItem, double* azimuth, double* distance, double* altDifference); void _calcPrevWaypointValues(double homeAlt, VisualMissionItem* currentItem, VisualMissionItem* prevItem, double* azimuth, double* distance, double* altDifference);
static double _calcDistanceToHome(VisualMissionItem* currentItem, VisualMissionItem* homeItem); static double _calcDistanceToHome(VisualMissionItem* currentItem, VisualMissionItem* homeItem);
bool _findPreviousAltitude(int newIndex, double* prevAltitude, MAV_FRAME* prevFrame); bool _findPreviousAltitude(int newIndex, double* prevAltitude, int* prevAltitudeMode);
static double _normalizeLat(double lat); static double _normalizeLat(double lat);
static double _normalizeLon(double lon); static double _normalizeLon(double lon);
void _addMissionSettings(QmlObjectListModel* visualItems, bool addToCenter); void _addMissionSettings(QmlObjectListModel* visualItems, bool addToCenter);

8
src/MissionManager/PlanMasterController.h

@ -47,10 +47,14 @@ public:
/// Should be called immediately upon Component.onCompleted. /// Should be called immediately upon Component.onCompleted.
/// @param editMode true: controller being used in Plan view, false: controller being used in Fly view /// @param editMode true: controller being used in Plan view, false: controller being used in Fly view
Q_INVOKABLE virtual void start(bool editMode); Q_INVOKABLE void start(bool editMode);
/// Starts the controller using a single static active vehicle. Will not track global active vehicle changes. /// Starts the controller using a single static active vehicle. Will not track global active vehicle changes.
Q_INVOKABLE virtual void startStaticActiveVehicle(Vehicle* vehicle); Q_INVOKABLE void startStaticActiveVehicle(Vehicle* vehicle);
/// Determines if the plan has all data needed to be saved or sent to the vehicle. Currently the only case where this
/// would return false is when it is still waiting on terrain data to determine correct altitudes.
Q_INVOKABLE bool readyForSaveSend(void) const { return _missionController.readyForSaveSend(); }
/// Sends a plan to the specified file /// Sends a plan to the specified file
/// @param[in] vehicle Vehicle we are sending a plan to /// @param[in] vehicle Vehicle we are sending a plan to

308
src/MissionManager/SimpleMissionItem.cc

@ -27,6 +27,10 @@ FactMetaData* SimpleMissionItem::_frameMetaData = NULL;
FactMetaData* SimpleMissionItem::_latitudeMetaData = NULL; FactMetaData* SimpleMissionItem::_latitudeMetaData = NULL;
FactMetaData* SimpleMissionItem::_longitudeMetaData = NULL; FactMetaData* SimpleMissionItem::_longitudeMetaData = NULL;
const char* SimpleMissionItem::_jsonAltitudeModeKey = "AltitudeMode";
const char* SimpleMissionItem::_jsonAltitudeKey = "Altitude";
const char* SimpleMissionItem::_jsonAMSLAltAboveTerrainKey = "AMSLAltAboveTerrain";
struct EnumInfo_s { struct EnumInfo_s {
const char * label; const char * label;
MAV_FRAME frame; MAV_FRAME frame;
@ -48,29 +52,28 @@ static const struct EnumInfo_s _rgMavFrameInfo[] = {
}; };
SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, QObject* parent) SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, QObject* parent)
: VisualMissionItem(vehicle, parent) : VisualMissionItem (vehicle, parent)
, _rawEdit(false) , _rawEdit (false)
, _dirty(false) , _dirty (false)
, _ignoreDirtyChangeSignals(false) , _ignoreDirtyChangeSignals (false)
, _speedSection(NULL) , _speedSection (NULL)
, _cameraSection(NULL) , _cameraSection (NULL)
, _commandTree(qgcApp()->toolbox()->missionCommandTree()) , _commandTree (qgcApp()->toolbox()->missionCommandTree())
, _altitudeRelativeToHomeFact (0, "Altitude is relative to home", FactMetaData::valueTypeUint32)
, _supportedCommandFact (0, "Command:", FactMetaData::valueTypeUint32) , _supportedCommandFact (0, "Command:", FactMetaData::valueTypeUint32)
, _param1MetaData(FactMetaData::valueTypeDouble) , _altitudeMode (AltitudeRelative)
, _param2MetaData(FactMetaData::valueTypeDouble) , _altitudeFact (0, "Altitude", FactMetaData::valueTypeDouble)
, _param3MetaData(FactMetaData::valueTypeDouble) , _amslAltAboveTerrainFact (0, "Alt above terrain", FactMetaData::valueTypeDouble)
, _param4MetaData(FactMetaData::valueTypeDouble) , _param1MetaData (FactMetaData::valueTypeDouble)
, _param5MetaData(FactMetaData::valueTypeDouble) , _param2MetaData (FactMetaData::valueTypeDouble)
, _param6MetaData(FactMetaData::valueTypeDouble) , _param3MetaData (FactMetaData::valueTypeDouble)
, _param7MetaData(FactMetaData::valueTypeDouble) , _param4MetaData (FactMetaData::valueTypeDouble)
, _syncingAltitudeRelativeToHomeAndFrame (false) , _param5MetaData (FactMetaData::valueTypeDouble)
, _param6MetaData (FactMetaData::valueTypeDouble)
, _param7MetaData (FactMetaData::valueTypeDouble)
, _syncingHeadingDegreesAndParam4 (false) , _syncingHeadingDegreesAndParam4 (false)
{ {
_editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml"); _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml");
_altitudeRelativeToHomeFact.setRawValue(true);
_setupMetaData(); _setupMetaData();
_connectSignals(); _connectSignals();
_updateOptionalSections(); _updateOptionalSections();
@ -81,35 +84,37 @@ SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, QObject* parent)
connect(&_missionItem, &MissionItem::specifiedFlightSpeedChanged, this, &SimpleMissionItem::specifiedFlightSpeedChanged); connect(&_missionItem, &MissionItem::specifiedFlightSpeedChanged, this, &SimpleMissionItem::specifiedFlightSpeedChanged);
connect(this, &SimpleMissionItem::sequenceNumberChanged, this, &SimpleMissionItem::lastSequenceNumberChanged); connect(this, &SimpleMissionItem::sequenceNumberChanged, this, &SimpleMissionItem::lastSequenceNumberChanged);
connect(this, &SimpleMissionItem::cameraSectionChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(this, &SimpleMissionItem::cameraSectionChanged, this, &SimpleMissionItem::_setDirty);
connect(this, &SimpleMissionItem::cameraSectionChanged, this, &SimpleMissionItem::_updateLastSequenceNumber); connect(this, &SimpleMissionItem::cameraSectionChanged, this, &SimpleMissionItem::_updateLastSequenceNumber);
} }
SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, bool editMode, const MissionItem& missionItem, QObject* parent) SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, bool editMode, const MissionItem& missionItem, QObject* parent)
: VisualMissionItem(vehicle, parent) : VisualMissionItem (vehicle, parent)
, _missionItem(missionItem) , _missionItem (missionItem)
, _rawEdit(false) , _rawEdit (false)
, _dirty(false) , _dirty (false)
, _ignoreDirtyChangeSignals(false) , _ignoreDirtyChangeSignals (false)
, _speedSection(NULL) , _speedSection (NULL)
, _cameraSection(NULL) , _cameraSection (NULL)
, _commandTree(qgcApp()->toolbox()->missionCommandTree()) , _commandTree (qgcApp()->toolbox()->missionCommandTree())
, _altitudeRelativeToHomeFact (0, "Altitude is relative to home", FactMetaData::valueTypeUint32)
, _supportedCommandFact (0, "Command:", FactMetaData::valueTypeUint32) , _supportedCommandFact (0, "Command:", FactMetaData::valueTypeUint32)
, _param1MetaData(FactMetaData::valueTypeDouble) , _altitudeMode (missionItem.relativeAltitude() ? AltitudeRelative : AltitudeAMSL)
, _param2MetaData(FactMetaData::valueTypeDouble) , _altitudeFact (0, "Altitude", FactMetaData::valueTypeDouble)
, _param3MetaData(FactMetaData::valueTypeDouble) , _amslAltAboveTerrainFact (0, "Alt above terrain", FactMetaData::valueTypeDouble)
, _param4MetaData(FactMetaData::valueTypeDouble) , _param1MetaData (FactMetaData::valueTypeDouble)
, _param5MetaData(FactMetaData::valueTypeDouble) , _param2MetaData (FactMetaData::valueTypeDouble)
, _param6MetaData(FactMetaData::valueTypeDouble) , _param3MetaData (FactMetaData::valueTypeDouble)
, _param7MetaData(FactMetaData::valueTypeDouble) , _param4MetaData (FactMetaData::valueTypeDouble)
, _syncingAltitudeRelativeToHomeAndFrame (false) , _param5MetaData (FactMetaData::valueTypeDouble)
, _param6MetaData (FactMetaData::valueTypeDouble)
, _param7MetaData (FactMetaData::valueTypeDouble)
, _syncingHeadingDegreesAndParam4 (false) , _syncingHeadingDegreesAndParam4 (false)
{ {
_editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml"); _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml");
_altitudeRelativeToHomeFact.setRawValue(true);
_isCurrentItem = missionItem.isCurrentItem(); _isCurrentItem = missionItem.isCurrentItem();
_altitudeFact.setRawValue(specifiesCoordinate() ? _missionItem._param7Fact.rawValue() : qQNaN());
_amslAltAboveTerrainFact.setRawValue(qQNaN());
// In !editMode we skip some of the intialization to save memory // In !editMode we skip some of the intialization to save memory
if (editMode) { if (editMode) {
@ -117,71 +122,62 @@ SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, bool editMode, const Miss
} }
_connectSignals(); _connectSignals();
_updateOptionalSections(); _updateOptionalSections();
_syncFrameToAltitudeRelativeToHome();
if (editMode) { if (editMode) {
_rebuildFacts(); _rebuildFacts();
} }
} }
SimpleMissionItem::SimpleMissionItem(const SimpleMissionItem& other, QObject* parent) SimpleMissionItem::SimpleMissionItem(const SimpleMissionItem& other, QObject* parent)
: VisualMissionItem(other, parent) : VisualMissionItem (other, parent)
, _missionItem(other._vehicle) , _missionItem (other._vehicle)
, _rawEdit(false) , _rawEdit (false)
, _dirty(false) , _dirty (false)
, _ignoreDirtyChangeSignals(false) , _ignoreDirtyChangeSignals (false)
, _speedSection(NULL) , _speedSection (NULL)
, _cameraSection(NULL) , _cameraSection (NULL)
, _commandTree(qgcApp()->toolbox()->missionCommandTree()) , _commandTree (qgcApp()->toolbox()->missionCommandTree())
, _altitudeRelativeToHomeFact (0, "Altitude is relative to home", FactMetaData::valueTypeUint32)
, _supportedCommandFact (0, "Command:", FactMetaData::valueTypeUint32) , _supportedCommandFact (0, "Command:", FactMetaData::valueTypeUint32)
, _param1MetaData(FactMetaData::valueTypeDouble) , _altitudeMode (other._altitudeMode)
, _param2MetaData(FactMetaData::valueTypeDouble) , _altitudeFact (0, "Altitude", FactMetaData::valueTypeDouble)
, _param3MetaData(FactMetaData::valueTypeDouble) , _amslAltAboveTerrainFact (qQNaN(), "Alt above terrain", FactMetaData::valueTypeDouble)
, _param4MetaData(FactMetaData::valueTypeDouble) , _param1MetaData (FactMetaData::valueTypeDouble)
, _syncingAltitudeRelativeToHomeAndFrame (false) , _param2MetaData (FactMetaData::valueTypeDouble)
, _param3MetaData (FactMetaData::valueTypeDouble)
, _param4MetaData (FactMetaData::valueTypeDouble)
, _syncingHeadingDegreesAndParam4 (false) , _syncingHeadingDegreesAndParam4 (false)
{ {
_editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml"); _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml");
_altitudeFact.setRawValue(other._altitudeFact.rawValue());
_amslAltAboveTerrainFact.setRawValue(other._amslAltAboveTerrainFact.rawValue());
_setupMetaData(); _setupMetaData();
_connectSignals(); _connectSignals();
_updateOptionalSections(); _updateOptionalSections();
*this = other;
_rebuildFacts(); _rebuildFacts();
} }
const SimpleMissionItem& SimpleMissionItem::operator=(const SimpleMissionItem& other)
{
VisualMissionItem::operator=(other);
setRawEdit(other._rawEdit);
setDirty(other._dirty);
setHomePositionSpecialCase(other._homePositionSpecialCase);
_syncFrameToAltitudeRelativeToHome();
_rebuildFacts();
return *this;
}
void SimpleMissionItem::_connectSignals(void) void SimpleMissionItem::_connectSignals(void)
{ {
// Connect to change signals to track dirty state // Connect to change signals to track dirty state
connect(&_missionItem._param1Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._param1Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem._param2Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._param2Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem._param3Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._param3Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem._param4Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._param4Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem._param5Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._param5Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem._param6Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._param6Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem._param7Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._param7Fact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem._frameFact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._frameFact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::_setDirty);
connect(&_missionItem, &MissionItem::sequenceNumberChanged, this, &SimpleMissionItem::_setDirtyFromSignal); connect(&_missionItem, &MissionItem::sequenceNumberChanged, this, &SimpleMissionItem::_setDirty);
connect(this, &SimpleMissionItem::altitudeModeChanged,this, &SimpleMissionItem::_setDirty);
// Values from these facts must propagate back and forth between the real object storage
connect(&_altitudeRelativeToHomeFact, &Fact::valueChanged, this, &SimpleMissionItem::_syncAltitudeRelativeToHomeToFrame); // These changes may need to trigger terrain queries
connect(&_missionItem._frameFact, &Fact::valueChanged, this, &SimpleMissionItem::_syncFrameToAltitudeRelativeToHome); connect(&_altitudeFact, &Fact::valueChanged, this, &SimpleMissionItem::_altitudeChanged);
connect(this, &SimpleMissionItem::altitudeModeChanged,this, &SimpleMissionItem::_altitudeModeChanged);
connect(this, &SimpleMissionItem::terrainAltitudeChanged,this, &SimpleMissionItem::_terrainAltChanged);
// These are coordinate parameters, they must emit coordinateChanged signal // These are coordinate parameters, they must emit coordinateChanged signal
connect(&_missionItem._param5Fact, &Fact::valueChanged, this, &SimpleMissionItem::_sendCoordinateChanged); connect(&_missionItem._param5Fact, &Fact::valueChanged, this, &SimpleMissionItem::_sendCoordinateChanged);
@ -208,7 +204,6 @@ void SimpleMissionItem::_connectSignals(void)
// These fact signals must alway signal out through SimpleMissionItem signals // These fact signals must alway signal out through SimpleMissionItem signals
connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::_sendCommandChanged); connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::_sendCommandChanged);
connect(&_missionItem._frameFact, &Fact::valueChanged, this, &SimpleMissionItem::_sendFrameChanged);
// Sequence number is kept in mission iteem, so we need to propagate signal up as well // Sequence number is kept in mission iteem, so we need to propagate signal up as well
connect(&_missionItem, &MissionItem::sequenceNumberChanged, this, &SimpleMissionItem::sequenceNumberChanged); connect(&_missionItem, &MissionItem::sequenceNumberChanged, this, &SimpleMissionItem::sequenceNumberChanged);
@ -260,6 +255,7 @@ void SimpleMissionItem::_setupMetaData(void)
_missionItem._commandFact.setMetaData(_commandMetaData); _missionItem._commandFact.setMetaData(_commandMetaData);
_missionItem._frameFact.setMetaData(_frameMetaData); _missionItem._frameFact.setMetaData(_frameMetaData);
_altitudeFact.setMetaData(_altitudeMetaData);
} }
SimpleMissionItem::~SimpleMissionItem() SimpleMissionItem::~SimpleMissionItem()
@ -276,6 +272,14 @@ void SimpleMissionItem::save(QJsonArray& missionItems)
MissionItem* item = items[i]; MissionItem* item = items[i];
QJsonObject saveObject; QJsonObject saveObject;
item->save(saveObject); item->save(saveObject);
if (i == 0) {
// This is the main simple item, save the alt/terrain data
if (specifiesCoordinate()) {
saveObject[_jsonAltitudeModeKey] = _altitudeMode;
saveObject[_jsonAltitudeKey] = _altitudeFact.rawValue().toDouble();
saveObject[_jsonAMSLAltAboveTerrainKey] = _amslAltAboveTerrainFact.rawValue().toDouble();
}
}
missionItems.append(saveObject); missionItems.append(saveObject);
item->deleteLater(); item->deleteLater();
} }
@ -285,6 +289,11 @@ bool SimpleMissionItem::load(QTextStream &loadStream)
{ {
bool success; bool success;
if ((success = _missionItem.load(loadStream))) { if ((success = _missionItem.load(loadStream))) {
if (specifiesCoordinate()) {
_altitudeMode = _missionItem.relativeAltitude() ? AltitudeRelative : AltitudeAMSL;
_altitudeFact.setRawValue(_missionItem._param7Fact.rawValue());
_amslAltAboveTerrainFact.setRawValue(qQNaN());
}
_updateOptionalSections(); _updateOptionalSections();
} }
return success; return success;
@ -292,11 +301,34 @@ bool SimpleMissionItem::load(QTextStream &loadStream)
bool SimpleMissionItem::load(const QJsonObject& json, int sequenceNumber, QString& errorString) bool SimpleMissionItem::load(const QJsonObject& json, int sequenceNumber, QString& errorString)
{ {
bool success; if (!_missionItem.load(json, sequenceNumber, errorString)) {
if ((success = _missionItem.load(json, sequenceNumber, errorString))) { return false;
_updateOptionalSections();
} }
return success;
if (specifiesCoordinate()) {
if (json.contains(_jsonAltitudeModeKey) || json.contains(_jsonAltitudeKey) || json.contains(_jsonAMSLAltAboveTerrainKey)) {
QList<JsonHelper::KeyValidateInfo> keyInfoList = {
{ _jsonAltitudeModeKey, QJsonValue::Double, true },
{ _jsonAltitudeKey, QJsonValue::Double, true },
{ _jsonAMSLAltAboveTerrainKey, QJsonValue::Double, true },
};
if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) {
return false;
}
_altitudeMode = (AltitudeMode)json[_jsonAltitudeModeKey].toDouble();
_altitudeFact.setRawValue(JsonHelper::possibleNaNJsonValue(json[_jsonAltitudeKey]));
_amslAltAboveTerrainFact.setRawValue(JsonHelper::possibleNaNJsonValue(json[_jsonAltitudeKey]));
} else {
_altitudeMode = _missionItem.relativeAltitude() ? AltitudeRelative : AltitudeAMSL;
_altitudeFact.setRawValue(_missionItem._param7Fact.rawValue());
_amslAltAboveTerrainFact.setRawValue(qQNaN());
}
}
_updateOptionalSections();
return true;
} }
bool SimpleMissionItem::isStandaloneCoordinate(void) const bool SimpleMissionItem::isStandaloneCoordinate(void) const
@ -429,12 +461,6 @@ void SimpleMissionItem::_rebuildTextFieldFacts(void)
} }
} }
if (uiInfo->specifiesCoordinate() || uiInfo->specifiesAltitudeOnly()) {
_missionItem._param7Fact._setName("Altitude");
_missionItem._param7Fact.setMetaData(_altitudeMetaData);
_textFieldFacts.append(&_missionItem._param7Fact);
}
_ignoreDirtyChangeSignals = false; _ignoreDirtyChangeSignals = false;
} }
} }
@ -489,15 +515,9 @@ void SimpleMissionItem::_rebuildNaNFacts(void)
} }
} }
void SimpleMissionItem::_rebuildCheckboxFacts(void) bool SimpleMissionItem::specifiesAltitude(void) const
{ {
_checkboxFacts.clear(); return specifiesCoordinate() || specifiesAltitudeOnly();
if (rawEdit()) {
_checkboxFacts.append(&_missionItem._autoContinueFact);
} else if ((specifiesCoordinate() || specifiesAltitudeOnly()) && !_homePositionSpecialCase) {
_checkboxFacts.append(&_altitudeRelativeToHomeFact);
}
} }
void SimpleMissionItem::_rebuildComboBoxFacts(void) void SimpleMissionItem::_rebuildComboBoxFacts(void)
@ -541,7 +561,6 @@ void SimpleMissionItem::_rebuildFacts(void)
{ {
_rebuildTextFieldFacts(); _rebuildTextFieldFacts();
_rebuildNaNFacts(); _rebuildNaNFacts();
_rebuildCheckboxFacts();
_rebuildComboBoxFacts(); _rebuildComboBoxFacts();
} }
@ -597,7 +616,7 @@ void SimpleMissionItem::setDirty(bool dirty)
} }
} }
void SimpleMissionItem::_setDirtyFromSignal(void) void SimpleMissionItem::_setDirty(void)
{ {
if (!_ignoreDirtyChangeSignals) { if (!_ignoreDirtyChangeSignals) {
setDirty(true); setDirty(true);
@ -609,30 +628,76 @@ void SimpleMissionItem::_sendCoordinateChanged(void)
emit coordinateChanged(coordinate()); emit coordinateChanged(coordinate());
} }
void SimpleMissionItem::_syncAltitudeRelativeToHomeToFrame(const QVariant& value) void SimpleMissionItem::_altitudeModeChanged(void)
{ {
if (!_syncingAltitudeRelativeToHomeAndFrame) { switch (_altitudeMode) {
_syncingAltitudeRelativeToHomeAndFrame = true; case AltitudeAboveTerrain:
_missionItem.setFrame(value.toBool() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL); // Terrain altitudes are AMSL
emit coordinateHasRelativeAltitudeChanged(value.toBool()); _missionItem.setFrame(MAV_FRAME_GLOBAL);
_syncingAltitudeRelativeToHomeAndFrame = false; // Clear any old values
_missionItem._param7Fact.setRawValue(qQNaN());
_amslAltAboveTerrainFact.setRawValue(qQNaN());
_altitudeChanged();
break;
case AltitudeAMSL:
_missionItem.setFrame(MAV_FRAME_GLOBAL);
break;
case AltitudeRelative:
_missionItem.setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT);
break;
}
emit coordinateHasRelativeAltitudeChanged(_altitudeMode == AltitudeRelative);
}
void SimpleMissionItem::_altitudeChanged(void)
{
if (!specifiesAltitude()) {
return;
}
if (_altitudeMode == AltitudeAboveTerrain) {
_terrainAltChanged();
} else {
_missionItem._param7Fact.setRawValue(_altitudeFact.rawValue());
} }
} }
void SimpleMissionItem::_syncFrameToAltitudeRelativeToHome(void) void SimpleMissionItem::_terrainAltChanged(void)
{ {
if (!_syncingAltitudeRelativeToHomeAndFrame) { if (!specifiesAltitude() || _altitudeMode != AltitudeAboveTerrain) {
_syncingAltitudeRelativeToHomeAndFrame = true; // We don't need terrain data
_altitudeRelativeToHomeFact.setRawValue(relativeAltitude()); return;
emit coordinateHasRelativeAltitudeChanged(_altitudeRelativeToHomeFact.rawValue().toBool());
_syncingAltitudeRelativeToHomeAndFrame = false;
} }
if (!qIsNaN(_amslAltAboveTerrainFact.rawValue().toDouble())) {
// We already have terrain values set. Don't do it again to prevent dirty bit changing.
return;
}
if (qIsNaN(terrainAltitude())) {
// Set NaNs to signal we are waiting on terrain data
_missionItem._param7Fact.setRawValue(qQNaN());
_amslAltAboveTerrainFact.setRawValue(qQNaN());
} else {
double aboveTerrain = terrainAltitude() + _altitudeFact.rawValue().toDouble();
_missionItem._param7Fact.setRawValue(aboveTerrain);
_amslAltAboveTerrainFact.setRawValue(aboveTerrain);
}
}
bool SimpleMissionItem::readyForSave(void) const
{
return !specifiesAltitude() || !qIsNaN(_missionItem._param7Fact.rawValue().toDouble());
} }
void SimpleMissionItem::setDefaultsForCommand(void) void SimpleMissionItem::setDefaultsForCommand(void)
{ {
// We set these global defaults first, then if there are param defaults they will get reset // We set these global defaults first, then if there are param defaults they will get reset
_missionItem.setParam7(qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble()); _altitudeMode = AltitudeRelative;
double defaultAlt = qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble();
_altitudeFact.setRawValue(defaultAlt);
_missionItem._param7Fact.setRawValue(defaultAlt);
_amslAltAboveTerrainFact.setRawValue(qQNaN());
MAV_CMD command = (MAV_CMD)this->command(); MAV_CMD command = (MAV_CMD)this->command();
const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command); const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command);
@ -667,11 +732,6 @@ void SimpleMissionItem::setDefaultsForCommand(void)
setRawEdit(false); setRawEdit(false);
} }
void SimpleMissionItem::_sendFrameChanged(void)
{
emit frameChanged(_missionItem.frame());
}
void SimpleMissionItem::_sendCommandChanged(void) void SimpleMissionItem::_sendCommandChanged(void)
{ {
emit commandChanged(command()); emit commandChanged(command());
@ -846,3 +906,11 @@ void SimpleMissionItem::setMissionFlightStatus(MissionController::MissionFlightS
} }
} }
} }
void SimpleMissionItem::setAltitudeMode(AltitudeMode altitudeMode)
{
if (altitudeMode != _altitudeMode) {
_altitudeMode = altitudeMode;
emit altitudeModeChanged();
}
}

51
src/MissionManager/SimpleMissionItem.h

@ -29,12 +29,21 @@ public:
~SimpleMissionItem(); ~SimpleMissionItem();
const SimpleMissionItem& operator=(const SimpleMissionItem& other); enum AltitudeMode {
AltitudeRelative,
AltitudeAMSL,
AltitudeAboveTerrain
};
Q_ENUM(AltitudeMode)
Q_PROPERTY(QString category READ category NOTIFY commandChanged) Q_PROPERTY(QString category READ category NOTIFY commandChanged)
Q_PROPERTY(bool friendlyEditAllowed READ friendlyEditAllowed NOTIFY friendlyEditAllowedChanged) Q_PROPERTY(bool friendlyEditAllowed READ friendlyEditAllowed NOTIFY friendlyEditAllowedChanged)
Q_PROPERTY(bool rawEdit READ rawEdit WRITE setRawEdit NOTIFY rawEditChanged) ///< true: raw item editing with all params Q_PROPERTY(bool rawEdit READ rawEdit WRITE setRawEdit NOTIFY rawEditChanged) ///< true: raw item editing with all params
Q_PROPERTY(bool relativeAltitude READ relativeAltitude NOTIFY frameChanged) Q_PROPERTY(bool specifiesAltitude READ specifiesAltitude NOTIFY commandChanged)
Q_PROPERTY(Fact* altitude READ altitude CONSTANT) ///< Altitude as specified by altitudeMode. Not necessarily true mission item altitude
Q_PROPERTY(AltitudeMode altitudeMode READ altitudeMode WRITE setAltitudeMode NOTIFY altitudeModeChanged)
Q_PROPERTY(Fact* amslAltAboveTerrain READ amslAltAboveTerrain CONSTANT) ///< Actual AMSL altitude for item if altitudeMode == AltitudeAboveTerrain
Q_PROPERTY(MavlinkQmlSingleton::Qml_MAV_CMD command READ command WRITE setCommand NOTIFY commandChanged) Q_PROPERTY(MavlinkQmlSingleton::Qml_MAV_CMD command READ command WRITE setCommand NOTIFY commandChanged)
/// Optional sections /// Optional sections
@ -42,7 +51,6 @@ public:
Q_PROPERTY(QObject* cameraSection READ cameraSection NOTIFY cameraSectionChanged) Q_PROPERTY(QObject* cameraSection READ cameraSection NOTIFY cameraSectionChanged)
// These properties are used to display the editing ui // These properties are used to display the editing ui
Q_PROPERTY(QmlObjectListModel* checkboxFacts READ checkboxFacts CONSTANT)
Q_PROPERTY(QmlObjectListModel* comboboxFacts READ comboboxFacts CONSTANT) Q_PROPERTY(QmlObjectListModel* comboboxFacts READ comboboxFacts CONSTANT)
Q_PROPERTY(QmlObjectListModel* textFieldFacts READ textFieldFacts CONSTANT) Q_PROPERTY(QmlObjectListModel* textFieldFacts READ textFieldFacts CONSTANT)
Q_PROPERTY(QmlObjectListModel* nanFacts READ nanFacts CONSTANT) Q_PROPERTY(QmlObjectListModel* nanFacts READ nanFacts CONSTANT)
@ -60,15 +68,20 @@ public:
MavlinkQmlSingleton::Qml_MAV_CMD command(void) const { return (MavlinkQmlSingleton::Qml_MAV_CMD)_missionItem._commandFact.cookedValue().toInt(); } MavlinkQmlSingleton::Qml_MAV_CMD command(void) const { return (MavlinkQmlSingleton::Qml_MAV_CMD)_missionItem._commandFact.cookedValue().toInt(); }
bool friendlyEditAllowed (void) const; bool friendlyEditAllowed (void) const;
bool rawEdit (void) const; bool rawEdit (void) const;
bool specifiesAltitude (void) const;
AltitudeMode altitudeMode (void) const { return _altitudeMode; }
Fact* altitude (void) { return &_altitudeFact; }
Fact* amslAltAboveTerrain (void) { return &_amslAltAboveTerrainFact; }
CameraSection* cameraSection (void) { return _cameraSection; } CameraSection* cameraSection (void) { return _cameraSection; }
SpeedSection* speedSection (void) { return _speedSection; } SpeedSection* speedSection (void) { return _speedSection; }
QmlObjectListModel* textFieldFacts (void) { return &_textFieldFacts; } QmlObjectListModel* textFieldFacts (void) { return &_textFieldFacts; }
QmlObjectListModel* nanFacts (void) { return &_nanFacts; } QmlObjectListModel* nanFacts (void) { return &_nanFacts; }
QmlObjectListModel* checkboxFacts (void) { return &_checkboxFacts; }
QmlObjectListModel* comboboxFacts (void) { return &_comboboxFacts; } QmlObjectListModel* comboboxFacts (void) { return &_comboboxFacts; }
void setRawEdit(bool rawEdit); void setRawEdit(bool rawEdit);
void setAltitudeMode(AltitudeMode altitudeMode);
void setCommandByIndex(int index); void setCommandByIndex(int index);
@ -82,8 +95,6 @@ public:
bool load(QTextStream &loadStream); bool load(QTextStream &loadStream);
bool load(const QJsonObject& json, int sequenceNumber, QString& errorString); bool load(const QJsonObject& json, int sequenceNumber, QString& errorString);
bool relativeAltitude(void) { return _missionItem.frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT; }
MissionItem& missionItem(void) { return _missionItem; } MissionItem& missionItem(void) { return _missionItem; }
const MissionItem& missionItem(void) const { return _missionItem; } const MissionItem& missionItem(void) const { return _missionItem; }
@ -107,6 +118,7 @@ public:
void appendMissionItems (QList<MissionItem*>& items, QObject* missionItemParent) final; void appendMissionItems (QList<MissionItem*>& items, QObject* missionItemParent) final;
void applyNewAltitude (double newAltitude) final; void applyNewAltitude (double newAltitude) final;
void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final;
bool readyForSave (void) const final;
bool coordinateHasRelativeAltitude (void) const final { return _missionItem.relativeAltitude(); } bool coordinateHasRelativeAltitude (void) const final { return _missionItem.relativeAltitude(); }
bool exitCoordinateHasRelativeAltitude (void) const final { return coordinateHasRelativeAltitude(); } bool exitCoordinateHasRelativeAltitude (void) const final { return coordinateHasRelativeAltitude(); }
@ -123,33 +135,31 @@ public slots:
signals: signals:
void commandChanged (int command); void commandChanged (int command);
void frameChanged (int frame);
void friendlyEditAllowedChanged (bool friendlyEditAllowed); void friendlyEditAllowedChanged (bool friendlyEditAllowed);
void headingDegreesChanged (double heading); void headingDegreesChanged (double heading);
void rawEditChanged (bool rawEdit); void rawEditChanged (bool rawEdit);
void cameraSectionChanged (QObject* cameraSection); void cameraSectionChanged (QObject* cameraSection);
void speedSectionChanged (QObject* cameraSection); void speedSectionChanged (QObject* cameraSection);
void altitudeModeChanged (void);
private slots: private slots:
void _setDirtyFromSignal (void); void _setDirty (void);
void _sectionDirtyChanged (bool dirty); void _sectionDirtyChanged (bool dirty);
void _sendCommandChanged (void); void _sendCommandChanged (void);
void _sendCoordinateChanged (void); void _sendCoordinateChanged (void);
void _sendFrameChanged (void); void _sendFriendlyEditAllowedChanged(void);
void _sendFriendlyEditAllowedChanged (void); void _altitudeChanged (void);
void _syncAltitudeRelativeToHomeToFrame (const QVariant& value); void _altitudeModeChanged (void);
void _syncFrameToAltitudeRelativeToHome (void); void _terrainAltChanged (void);
void _updateLastSequenceNumber (void); void _updateLastSequenceNumber (void);
void _rebuildFacts (void); void _rebuildFacts (void);
void _rebuildTextFieldFacts (void);
private: private:
void _connectSignals (void); void _connectSignals (void);
void _setupMetaData (void); void _setupMetaData (void);
void _updateOptionalSections(void); void _updateOptionalSections(void);
void _rebuildTextFieldFacts (void);
void _rebuildNaNFacts (void); void _rebuildNaNFacts (void);
void _rebuildCheckboxFacts (void);
void _rebuildComboBoxFacts (void); void _rebuildComboBoxFacts (void);
MissionItem _missionItem; MissionItem _missionItem;
@ -162,12 +172,14 @@ private:
MissionCommandTree* _commandTree; MissionCommandTree* _commandTree;
Fact _altitudeRelativeToHomeFact;
Fact _supportedCommandFact; Fact _supportedCommandFact;
AltitudeMode _altitudeMode;
Fact _altitudeFact;
Fact _amslAltAboveTerrainFact;
QmlObjectListModel _textFieldFacts; QmlObjectListModel _textFieldFacts;
QmlObjectListModel _nanFacts; QmlObjectListModel _nanFacts;
QmlObjectListModel _checkboxFacts;
QmlObjectListModel _comboboxFacts; QmlObjectListModel _comboboxFacts;
static FactMetaData* _altitudeMetaData; static FactMetaData* _altitudeMetaData;
@ -185,8 +197,11 @@ private:
FactMetaData _param6MetaData; FactMetaData _param6MetaData;
FactMetaData _param7MetaData; FactMetaData _param7MetaData;
bool _syncingAltitudeRelativeToHomeAndFrame; ///< true: already in a sync signal, prevents signal loop
bool _syncingHeadingDegreesAndParam4; ///< true: already in a sync signal, prevents signal loop bool _syncingHeadingDegreesAndParam4; ///< true: already in a sync signal, prevents signal loop
static const char* _jsonAltitudeModeKey;
static const char* _jsonAltitudeKey;
static const char* _jsonAMSLAltAboveTerrainKey;
}; };
#endif #endif

68
src/MissionManager/SimpleMissionItemTest.cc

@ -15,43 +15,37 @@
const SimpleMissionItemTest::ItemInfo_t SimpleMissionItemTest::_rgItemInfo[] = { const SimpleMissionItemTest::ItemInfo_t SimpleMissionItemTest::_rgItemInfo[] = {
{ MAV_CMD_NAV_WAYPOINT, MAV_FRAME_GLOBAL_RELATIVE_ALT }, { MAV_CMD_NAV_WAYPOINT, MAV_FRAME_GLOBAL_RELATIVE_ALT },
{ MAV_CMD_NAV_LOITER_UNLIM, MAV_FRAME_GLOBAL_RELATIVE_ALT }, { MAV_CMD_NAV_LOITER_UNLIM, MAV_FRAME_GLOBAL },
{ MAV_CMD_NAV_LOITER_TURNS, MAV_FRAME_GLOBAL_RELATIVE_ALT }, { MAV_CMD_NAV_LOITER_TURNS, MAV_FRAME_GLOBAL_RELATIVE_ALT },
{ MAV_CMD_NAV_LOITER_TIME, MAV_FRAME_GLOBAL_RELATIVE_ALT }, { MAV_CMD_NAV_LOITER_TIME, MAV_FRAME_GLOBAL },
{ MAV_CMD_NAV_LAND, MAV_FRAME_GLOBAL_RELATIVE_ALT }, { MAV_CMD_NAV_LAND, MAV_FRAME_GLOBAL_RELATIVE_ALT },
{ MAV_CMD_NAV_TAKEOFF, MAV_FRAME_GLOBAL_RELATIVE_ALT }, { MAV_CMD_NAV_TAKEOFF, MAV_FRAME_GLOBAL },
{ MAV_CMD_DO_JUMP, MAV_FRAME_MISSION }, { MAV_CMD_DO_JUMP, MAV_FRAME_MISSION },
}; };
const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesWaypoint[] = { const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesWaypoint[] = {
{ "Altitude", 70.1234567 },
{ "Hold", 10.1234567 }, { "Hold", 10.1234567 },
}; };
const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesLoiterUnlim[] = { const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesLoiterUnlim[] = {
{ "Altitude", 70.1234567 },
{ "Radius", 30.1234567 }, { "Radius", 30.1234567 },
}; };
const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesLoiterTurns[] = { const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesLoiterTurns[] = {
{ "Altitude", 70.1234567 },
{ "Radius", 30.1234567 }, { "Radius", 30.1234567 },
{ "Turns", 10.1234567 }, { "Turns", 10.1234567 },
}; };
const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesLoiterTime[] = { const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesLoiterTime[] = {
{ "Altitude", 70.1234567 },
{ "Radius", 30.1234567 }, { "Radius", 30.1234567 },
{ "Hold", 10.1234567 }, { "Hold", 10.1234567 },
}; };
const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesLand[] = { const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesLand[] = {
{ "Altitude", 70.1234567 },
}; };
const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesTakeoff[] = { const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesTakeoff[] = {
{ "Pitch", 10.1234567 }, { "Pitch", 10.1234567 },
{ "Altitude", 70.1234567 },
}; };
const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesDoJump[] = { const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesDoJump[] = {
@ -60,13 +54,14 @@ const SimpleMissionItemTest::FactValue_t SimpleMissionItemTest::_rgFactValuesDoJ
}; };
const SimpleMissionItemTest::ItemExpected_t SimpleMissionItemTest::_rgItemExpected[] = { const SimpleMissionItemTest::ItemExpected_t SimpleMissionItemTest::_rgItemExpected[] = {
{ sizeof(SimpleMissionItemTest::_rgFactValuesWaypoint)/sizeof(SimpleMissionItemTest::_rgFactValuesWaypoint[0]), SimpleMissionItemTest::_rgFactValuesWaypoint, true }, // Text field facts count Fact Values Altitude Altitude Mode
{ sizeof(SimpleMissionItemTest::_rgFactValuesLoiterUnlim)/sizeof(SimpleMissionItemTest::_rgFactValuesLoiterUnlim[0]), SimpleMissionItemTest::_rgFactValuesLoiterUnlim, true }, { sizeof(SimpleMissionItemTest::_rgFactValuesWaypoint)/sizeof(SimpleMissionItemTest::_rgFactValuesWaypoint[0]), SimpleMissionItemTest::_rgFactValuesWaypoint, 70.1234567, SimpleMissionItem::AltitudeRelative },
{ sizeof(SimpleMissionItemTest::_rgFactValuesLoiterTurns)/sizeof(SimpleMissionItemTest::_rgFactValuesLoiterTurns[0]), SimpleMissionItemTest::_rgFactValuesLoiterTurns, true }, { sizeof(SimpleMissionItemTest::_rgFactValuesLoiterUnlim)/sizeof(SimpleMissionItemTest::_rgFactValuesLoiterUnlim[0]), SimpleMissionItemTest::_rgFactValuesLoiterUnlim, 70.1234567, SimpleMissionItem::AltitudeAMSL },
{ sizeof(SimpleMissionItemTest::_rgFactValuesLoiterTime)/sizeof(SimpleMissionItemTest::_rgFactValuesLoiterTime[0]), SimpleMissionItemTest::_rgFactValuesLoiterTime, true }, { sizeof(SimpleMissionItemTest::_rgFactValuesLoiterTurns)/sizeof(SimpleMissionItemTest::_rgFactValuesLoiterTurns[0]), SimpleMissionItemTest::_rgFactValuesLoiterTurns, 70.1234567, SimpleMissionItem::AltitudeRelative },
{ sizeof(SimpleMissionItemTest::_rgFactValuesLand)/sizeof(SimpleMissionItemTest::_rgFactValuesLand[0]), SimpleMissionItemTest::_rgFactValuesLand, true }, { sizeof(SimpleMissionItemTest::_rgFactValuesLoiterTime)/sizeof(SimpleMissionItemTest::_rgFactValuesLoiterTime[0]), SimpleMissionItemTest::_rgFactValuesLoiterTime, 70.1234567, SimpleMissionItem::AltitudeAMSL },
{ sizeof(SimpleMissionItemTest::_rgFactValuesTakeoff)/sizeof(SimpleMissionItemTest::_rgFactValuesTakeoff[0]), SimpleMissionItemTest::_rgFactValuesTakeoff, true }, { sizeof(SimpleMissionItemTest::_rgFactValuesLand)/sizeof(SimpleMissionItemTest::_rgFactValuesLand[0]), SimpleMissionItemTest::_rgFactValuesLand, 70.1234567, SimpleMissionItem::AltitudeRelative },
{ sizeof(SimpleMissionItemTest::_rgFactValuesDoJump)/sizeof(SimpleMissionItemTest::_rgFactValuesDoJump[0]), SimpleMissionItemTest::_rgFactValuesDoJump, false }, { sizeof(SimpleMissionItemTest::_rgFactValuesTakeoff)/sizeof(SimpleMissionItemTest::_rgFactValuesTakeoff[0]), SimpleMissionItemTest::_rgFactValuesTakeoff, 70.1234567, SimpleMissionItem::AltitudeAMSL },
{ sizeof(SimpleMissionItemTest::_rgFactValuesDoJump)/sizeof(SimpleMissionItemTest::_rgFactValuesDoJump[0]), SimpleMissionItemTest::_rgFactValuesDoJump, qQNaN(), SimpleMissionItem::AltitudeRelative },
}; };
SimpleMissionItemTest::SimpleMissionItemTest(void) SimpleMissionItemTest::SimpleMissionItemTest(void)
@ -80,12 +75,12 @@ void SimpleMissionItemTest::init(void)
VisualMissionItemTest::init(); VisualMissionItemTest::init();
rgSimpleItemSignals[commandChangedIndex] = SIGNAL(commandChanged(int)); rgSimpleItemSignals[commandChangedIndex] = SIGNAL(commandChanged(int));
rgSimpleItemSignals[frameChangedIndex] = SIGNAL(frameChanged(int)); rgSimpleItemSignals[altitudeModeChangedIndex] = SIGNAL(altitudeModeChanged());
rgSimpleItemSignals[friendlyEditAllowedChangedIndex] = SIGNAL(friendlyEditAllowedChanged(bool)); rgSimpleItemSignals[friendlyEditAllowedChangedIndex] = SIGNAL(friendlyEditAllowedChanged(bool));
rgSimpleItemSignals[headingDegreesChangedIndex] = SIGNAL(headingDegreesChanged(double)); rgSimpleItemSignals[headingDegreesChangedIndex] = SIGNAL(headingDegreesChanged(double));
rgSimpleItemSignals[rawEditChangedIndex] = SIGNAL(rawEditChanged(bool)); rgSimpleItemSignals[rawEditChangedIndex] = SIGNAL(rawEditChanged(bool));
rgSimpleItemSignals[cameraSectionChangedIndex] = SIGNAL(rawEditChanged(bool)); rgSimpleItemSignals[cameraSectionChangedIndex] = SIGNAL(cameraSectionChanged(QObject*));
rgSimpleItemSignals[speedSectionChangedIndex] = SIGNAL(rawEditChanged(bool)); rgSimpleItemSignals[speedSectionChangedIndex] = SIGNAL(speedSectionChanged(QObject*));
rgSimpleItemSignals[coordinateHasRelativeAltitudeChangedIndex] = SIGNAL(coordinateHasRelativeAltitudeChanged(bool)); rgSimpleItemSignals[coordinateHasRelativeAltitudeChangedIndex] = SIGNAL(coordinateHasRelativeAltitudeChanged(bool));
MissionItem missionItem(1, // sequence number MissionItem missionItem(1, // sequence number
@ -164,8 +159,10 @@ void SimpleMissionItemTest::_testEditorFacts(void)
} }
QCOMPARE(factCount, expected->cFactValues); QCOMPARE(factCount, expected->cFactValues);
int expectedCount = expected->relativeAltCheckbox ? 1 : 0; if (!qIsNaN(expected->altValue)) {
QCOMPARE(simpleMissionItem.checkboxFacts()->count(), expectedCount); QCOMPARE(simpleMissionItem.altitudeMode(), expected->altMode);
QCOMPARE(simpleMissionItem.altitude()->rawValue().toDouble(), expected->altValue);
}
} }
delete vehicle; delete vehicle;
@ -228,18 +225,8 @@ void SimpleMissionItemTest::_testSignals(void)
QVERIFY(_spyVisualItem->checkOnlySignalByMask(dirtyChangedMask)); QVERIFY(_spyVisualItem->checkOnlySignalByMask(dirtyChangedMask));
_spyVisualItem->clearAllSignals(); _spyVisualItem->clearAllSignals();
// Check frameChanged signalling. Calling setFrame should signal: _simpleItem->setAltitudeMode(_simpleItem->altitudeMode() == SimpleMissionItem::AltitudeRelative ? SimpleMissionItem::AltitudeAMSL : SimpleMissionItem::AltitudeRelative);
// frameChanged QVERIFY(_spySimpleItem->checkOnlySignalByMask(dirtyChangedMask | friendlyEditAllowedChangedMask | altitudeModeChangedMask | coordinateHasRelativeAltitudeChangedMask));
// dirtyChanged
// friendlyEditAllowedChanged - this signal is not very smart on when it gets sent
// coordinateHasRelativeAltitudeChanged
missionItem.setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT);
QVERIFY(_spyVisualItem->checkNoSignals());
QVERIFY(_spySimpleItem->checkNoSignals());
missionItem.setFrame(MAV_FRAME_GLOBAL);
QVERIFY(_spySimpleItem->checkOnlySignalByMask(frameChangedMask | dirtyChangedMask | friendlyEditAllowedChangedMask | coordinateHasRelativeAltitudeChangedMask));
_spySimpleItem->clearAllSignals(); _spySimpleItem->clearAllSignals();
_spyVisualItem->clearAllSignals(); _spyVisualItem->clearAllSignals();
@ -320,3 +307,18 @@ void SimpleMissionItemTest::_testSpeedSection(void)
QCOMPARE(_spyVisualItem->checkSignalsByMask(specifiedFlightSpeedChangedMask), true); QCOMPARE(_spyVisualItem->checkSignalsByMask(specifiedFlightSpeedChangedMask), true);
QCOMPARE(_simpleItem->dirty(), true); QCOMPARE(_simpleItem->dirty(), true);
} }
void SimpleMissionItemTest::_testAltitudePropogation(void)
{
// Make sure that changes to altitude propogate to param 7 of the mission item
_simpleItem->setAltitudeMode(SimpleMissionItem::AltitudeRelative);
_simpleItem->altitude()->setRawValue(_simpleItem->altitude()->rawValue().toDouble() + 1);
QCOMPARE(_simpleItem->altitude()->rawValue().toDouble(), _simpleItem->missionItem().param7());
QCOMPARE(_simpleItem->missionItem().frame(), MAV_FRAME_GLOBAL_RELATIVE_ALT);
_simpleItem->setAltitudeMode(SimpleMissionItem::AltitudeAMSL);
_simpleItem->altitude()->setRawValue(_simpleItem->altitude()->rawValue().toDouble() + 1);
QCOMPARE(_simpleItem->altitude()->rawValue().toDouble(), _simpleItem->missionItem().param7());
QCOMPARE(_simpleItem->missionItem().frame(), MAV_FRAME_GLOBAL);
}

8
src/MissionManager/SimpleMissionItemTest.h

@ -31,11 +31,12 @@ private slots:
void _testSpeedSectionDirty(void); void _testSpeedSectionDirty(void);
void _testCameraSection(void); void _testCameraSection(void);
void _testSpeedSection(void); void _testSpeedSection(void);
void _testAltitudePropogation(void);
private: private:
enum { enum {
commandChangedIndex = 0, commandChangedIndex = 0,
frameChangedIndex, altitudeModeChangedIndex,
friendlyEditAllowedChangedIndex, friendlyEditAllowedChangedIndex,
headingDegreesChangedIndex, headingDegreesChangedIndex,
rawEditChangedIndex, rawEditChangedIndex,
@ -47,7 +48,7 @@ private:
enum { enum {
commandChangedMask = 1 << commandChangedIndex, commandChangedMask = 1 << commandChangedIndex,
frameChangedMask = 1 << frameChangedIndex, altitudeModeChangedMask = 1 << altitudeModeChangedIndex,
friendlyEditAllowedChangedMask = 1 << friendlyEditAllowedChangedIndex, friendlyEditAllowedChangedMask = 1 << friendlyEditAllowedChangedIndex,
headingDegreesChangedMask = 1 << headingDegreesChangedIndex, headingDegreesChangedMask = 1 << headingDegreesChangedIndex,
rawEditChangedMask = 1 << rawEditChangedIndex, rawEditChangedMask = 1 << rawEditChangedIndex,
@ -72,7 +73,8 @@ private:
typedef struct { typedef struct {
size_t cFactValues; size_t cFactValues;
const FactValue_t* rgFactValues; const FactValue_t* rgFactValues;
bool relativeAltCheckbox; double altValue;
SimpleMissionItem::AltitudeMode altMode;
} ItemExpected_t; } ItemExpected_t;
SimpleMissionItem* _simpleItem; SimpleMissionItem* _simpleItem;

5
src/MissionManager/VisualMissionItem.h

@ -130,6 +130,11 @@ public:
virtual void setSequenceNumber (int sequenceNumber) = 0; virtual void setSequenceNumber (int sequenceNumber) = 0;
virtual int lastSequenceNumber (void) const = 0; virtual int lastSequenceNumber (void) const = 0;
/// Specifies whether the item has all the data it needs such that it can be saved. Currently the only
/// case where this returns false is if it has not determined terrain values yet.
/// @return true: Ready to save, false: Still waiting on information
virtual bool readyForSave(void) const { return true; }
/// Save the item(s) in Json format /// Save the item(s) in Json format
/// @param missionItems Current set of mission items, new items should be appended to the end /// @param missionItems Current set of mission items, new items should be appended to the end
virtual void save(QJsonArray& missionItems) = 0; virtual void save(QJsonArray& missionItems) = 0;

16
src/PlanView/PlanView.qml

@ -162,7 +162,15 @@ QGCView {
_missionController.setCurrentPlanViewIndex(0, true) _missionController.setCurrentPlanViewIndex(0, true)
} }
function waitingOnDataMessage() {
_qgcView.showMessage(qsTr("Unable to Save/Upload"), qsTr("Plan is waiting on terrain data from server for correct altitude values."), StandardButton.Ok)
}
function upload() { function upload() {
if (!readyForSaveSend()) {
waitingOnDataMessage()
return
}
if (_activeVehicle && _activeVehicle.armed && _activeVehicle.flightMode === _activeVehicle.missionFlightMode) { if (_activeVehicle && _activeVehicle.armed && _activeVehicle.flightMode === _activeVehicle.missionFlightMode) {
_qgcView.showDialog(activeMissionUploadDialogComponent, qsTr("Plan Upload"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel) _qgcView.showDialog(activeMissionUploadDialogComponent, qsTr("Plan Upload"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
} else { } else {
@ -178,6 +186,10 @@ QGCView {
} }
function saveToSelectedFile() { function saveToSelectedFile() {
if (!readyForSaveSend()) {
waitingOnDataMessage()
return
}
fileDialog.title = qsTr("Save Plan") fileDialog.title = qsTr("Save Plan")
fileDialog.plan = true fileDialog.plan = true
fileDialog.selectExisting = false fileDialog.selectExisting = false
@ -190,6 +202,10 @@ QGCView {
} }
function saveKmlToSelectedFile() { function saveKmlToSelectedFile() {
if (!readyForSaveSend()) {
waitingOnDataMessage()
return
}
fileDialog.title = qsTr("Save KML") fileDialog.title = qsTr("Save KML")
fileDialog.plan = false fileDialog.plan = false
fileDialog.selectExisting = false fileDialog.selectExisting = false

46
src/PlanView/SimpleItemEditor.qml

@ -17,6 +17,9 @@ Rectangle {
color: qgcPal.windowShadeDark color: qgcPal.windowShadeDark
radius: _radius radius: _radius
property bool _specifiesAltitude: missionItem.specifiesAltitude
property bool _altModeIsTerrain: missionItem.altitudeMode === 2
Column { Column {
id: valuesColumn id: valuesColumn
anchors.margins: _margin anchors.margins: _margin
@ -68,9 +71,27 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
flow: GridLayout.TopToBottom flow: GridLayout.TopToBottom
rows: missionItem.textFieldFacts.count + missionItem.nanFacts.count + (missionItem.speedSection.available ? 1 : 0) rows: missionItem.textFieldFacts.count +
missionItem.nanFacts.count +
(missionItem.speedSection.available ? 1 : 0) +
(_specifiesAltitude ? 1 : 0) +
(_altModeIsTerrain ? 1 : 0)
columns: 2 columns: 2
QGCComboBox {
id: altCombo
model: [ qsTr("Alt (Rel)"), qsTr("AMSL"), qsTr("Above Terrain") ]
currentIndex: missionItem.altitudeMode
Layout.fillWidth: true
onActivated: missionItem.altitudeMode = index
visible: _specifiesAltitude
}
QGCLabel {
text: qsTr("Actual AMSL Alt")
visible: _altModeIsTerrain
}
Repeater { Repeater {
model: missionItem.textFieldFacts model: missionItem.textFieldFacts
@ -95,6 +116,19 @@ Rectangle {
visible: missionItem.speedSection.available visible: missionItem.speedSection.available
} }
FactTextField {
showUnits: true
fact: missionItem.altitude
Layout.fillWidth: true
visible: _specifiesAltitude
}
FactLabel {
fact: missionItem.amslAltAboveTerrain
visible: _altModeIsTerrain
}
Repeater { Repeater {
model: missionItem.textFieldFacts model: missionItem.textFieldFacts
@ -102,6 +136,7 @@ Rectangle {
showUnits: true showUnits: true
fact: object fact: object
Layout.fillWidth: true Layout.fillWidth: true
enabled: !object.readOnly
} }
} }
@ -124,15 +159,6 @@ Rectangle {
} }
} }
Repeater {
model: missionItem.checkboxFacts
FactCheckBox {
text: object.name
fact: object
}
}
CameraSection { CameraSection {
checked: missionItem.cameraSection.settingsSpecified checked: missionItem.cameraSection.settingsSpecified
visible: missionItem.cameraSection.available visible: missionItem.cameraSection.available

2
src/Terrain.cc

@ -77,7 +77,7 @@ void TerrainBatchManager::_sendNextBatch(void)
QUrlQuery query; QUrlQuery query;
query.addQueryItem(QStringLiteral("points"), points); query.addQueryItem(QStringLiteral("points"), points);
QUrl url(QStringLiteral("https://api.airmap.com/elevation/stage/srtm1/ele")); QUrl url(QStringLiteral("https://api.airmap.com/elevation/v1/ele"));
url.setQuery(query); url.setQuery(query);
QNetworkRequest request(url); QNetworkRequest request(url);

Loading…
Cancel
Save