diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 275792480381e0fe56ae3da0c9d6178f06da38e0..59ba9ad9026e113c378348b6bd982c7bd19126d7 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -781,6 +781,15 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec nextSequenceNumber = corridorItem->lastSequenceNumber() + 1; qCDebug(MissionControllerLog) << "Corridor Scan load complete: nextSequenceNumber" << nextSequenceNumber; visualItems->append(corridorItem); + } else if (complexItemType == CircularSurveyComplexItem::jsonComplexItemTypeValue) { + qCDebug(MissionControllerLog) << "Loading Circular Survey: nextSequenceNumber" << nextSequenceNumber; + CircularSurveyComplexItem* circularSurvey = new CircularSurveyComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, visualItems); + if (!circularSurvey->load(itemObject, nextSequenceNumber++, errorString)) { + return false; + } + nextSequenceNumber = circularSurvey->lastSequenceNumber() + 1; + qCDebug(MissionControllerLog) << "Circular Survey load complete: nextSequenceNumber" << nextSequenceNumber; + visualItems->append(circularSurvey); } else if (complexItemType == MissionSettingsItem::jsonComplexItemTypeValue) { qCDebug(MissionControllerLog) << "Loading Mission Settings: nextSequenceNumber" << nextSequenceNumber; MissionSettingsItem* settingsItem = new MissionSettingsItem(_controllerVehicle, _flyView, visualItems); diff --git a/src/PlanView/CircularSurveyItemEditor.qml b/src/PlanView/CircularSurveyItemEditor.qml index f7f8eada54cb4e0a8732a303845a13f583b88842..6497b770d5207436d4ef90a1a56d93f3cf0dacd9 100644 --- a/src/PlanView/CircularSurveyItemEditor.qml +++ b/src/PlanView/CircularSurveyItemEditor.qml @@ -72,6 +72,13 @@ Rectangle { visible: transectsHeader.checked + QGCLabel { text: qsTr("Altitude") } + FactTextField { + fact: missionItem.cameraCalc.distanceToSurface + Layout.fillWidth: true + //onUpdated: rSlider.value = missionItem.deltaR.value + } + QGCLabel { text: qsTr("Delta R") } FactTextField { fact: missionItem.deltaR diff --git a/src/Wima/CircularSurveyComplexItem.cc b/src/Wima/CircularSurveyComplexItem.cc index d6cd83206806b587a013eae898cb8c44df543c61..f1856e8fa6be0904c371373a576d859518857b35 100644 --- a/src/Wima/CircularSurveyComplexItem.cc +++ b/src/Wima/CircularSurveyComplexItem.cc @@ -1,10 +1,18 @@ #include "CircularSurveyComplexItem.h" - +#include "JsonHelper.h" +#include "QGCApplication.h" const char* CircularSurveyComplexItem::settingsGroup = "CircularSurvey"; const char* CircularSurveyComplexItem::deltaRName = "DeltaR"; const char* CircularSurveyComplexItem::deltaAlphaName = "DeltaAlpha"; +const char* CircularSurveyComplexItem::jsonComplexItemTypeValue = "circularSurvey"; +const char* CircularSurveyComplexItem::jsonDeltaRKey = "deltaR"; +const char* CircularSurveyComplexItem::jsonDeltaAlphaKey = "deltaAlpha"; +const char* CircularSurveyComplexItem::jsonReferencePointLatKey = "referencePointLat"; +const char* CircularSurveyComplexItem::jsonReferencePointLongKey = "referencePointLong"; +const char* CircularSurveyComplexItem::jsonReferencePointAltKey = "referencePointAlt"; + CircularSurveyComplexItem::CircularSurveyComplexItem(Vehicle *vehicle, bool flyView, const QString &kmlOrShpFile, QObject *parent) : TransectStyleComplexItem (vehicle, flyView, settingsGroup, parent) , _referencePoint (QGeoCoordinate(0, 0,0)) @@ -69,22 +77,243 @@ bool CircularSurveyComplexItem::autoGenerated() bool CircularSurveyComplexItem::load(const QJsonObject &complexObject, int sequenceNumber, QString &errorString) { - return false; + // We need to pull version first to determine what validation/conversion needs to be performed + QList versionKeyInfoList = { + { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, + }; + if (!JsonHelper::validateKeys(complexObject, versionKeyInfoList, errorString)) { + return false; + } + + int version = complexObject[JsonHelper::jsonVersionKey].toInt(); + if (version != 1) { + errorString = tr("Survey items do not support version %1").arg(version); + return false; + } + + QList keyInfoList = { + { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, + { ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true }, + { jsonDeltaRKey, QJsonValue::Double, true }, + { jsonDeltaAlphaKey, QJsonValue::Double, true }, + { jsonReferencePointLatKey, QJsonValue::Double, true }, + { jsonReferencePointLongKey, QJsonValue::Double, true }, + { jsonReferencePointAltKey, QJsonValue::Double, true }, + }; + + + if (!JsonHelper::validateKeys(complexObject, keyInfoList, errorString)) { + return false; + } + + QString itemType = complexObject[VisualMissionItem::jsonTypeKey].toString(); + QString complexType = complexObject[ComplexMissionItem::jsonComplexItemTypeKey].toString(); + if (itemType != VisualMissionItem::jsonTypeComplexItemValue || complexType != jsonComplexItemTypeValue) { + errorString = tr("%1 does not support loading this complex mission item type: %2:%3").arg(qgcApp()->applicationName()).arg(itemType).arg(complexType); + return false; + } + + _ignoreRecalc = true; + + setSequenceNumber(sequenceNumber); + + if (!_surveyAreaPolygon.loadFromJson(complexObject, true /* required */, errorString)) { + _surveyAreaPolygon.clear(); + return false; + } + + if (!_load(complexObject, errorString)) { + _ignoreRecalc = false; + return false; + } + + _deltaR.setRawValue (complexObject[jsonDeltaRKey].toDouble()); + _deltaAlpha.setRawValue (complexObject[jsonDeltaAlphaKey].toDouble()); + _referencePoint.setLongitude (complexObject[jsonReferencePointLongKey].toDouble()); + _referencePoint.setLatitude (complexObject[jsonReferencePointLatKey].toDouble()); + _referencePoint.setAltitude (complexObject[jsonReferencePointAltKey].toDouble()); + _autoGenerated = true; + + _ignoreRecalc = false; + + _recalcComplexDistance(); + if (_cameraShots == 0) { + // Shot count was possibly not available from plan file + _recalcCameraShots(); + } + + return true; } void CircularSurveyComplexItem::save(QJsonArray &planItems) { + QJsonObject saveObject; + + _save(saveObject); + + saveObject[JsonHelper::jsonVersionKey] = 1; + saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; + saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; + saveObject[jsonDeltaRKey] = _deltaR.rawValue().toDouble(); + saveObject[jsonDeltaAlphaKey] = _deltaAlpha.rawValue().toDouble(); + saveObject[jsonReferencePointLongKey] = _referencePoint.longitude(); + saveObject[jsonReferencePointLatKey] = _referencePoint.latitude(); + saveObject[jsonReferencePointAltKey] = _referencePoint.altitude(); + + // Polygon shape + _surveyAreaPolygon.saveToJson(saveObject); + + planItems.append(saveObject); } void CircularSurveyComplexItem::appendMissionItems(QList &items, QObject *missionItemParent) { + if (_loadedMissionItems.count()) { + // We have mission items from the loaded plan, use those + _appendLoadedMissionItems(items, missionItemParent); + } else { + // Build the mission items on the fly + _buildAndAppendMissionItems(items, missionItemParent); + } +} + +void CircularSurveyComplexItem::_appendLoadedMissionItems(QList& items, QObject* missionItemParent) +{ + //qCDebug(SurveyComplexItemLog) << "_appendLoadedMissionItems"; + int seqNum = _sequenceNumber; + + for (const MissionItem* loadedMissionItem: _loadedMissionItems) { + MissionItem* item = new MissionItem(*loadedMissionItem, missionItemParent); + item->setSequenceNumber(seqNum++); + items.append(item); + } } -void CircularSurveyComplexItem::applyNewAltitude(double newAltitude) +void CircularSurveyComplexItem::_buildAndAppendMissionItems(QList& items, QObject* missionItemParent) { + // original code: SurveyComplexItem::_buildAndAppendMissionItems() + //qCDebug(SurveyComplexItemLog) << "_buildAndAppendMissionItems"; + + // Now build the mission items from the transect points + + MissionItem* item; + int seqNum = _sequenceNumber; + // bool imagesEverywhere = _cameraTriggerInTurnAroundFact.rawValue().toBool(); + // bool addTriggerAtBeginning = !hoverAndCaptureEnabled() && imagesEverywhere; + bool firstOverallPoint = true; + + MAV_FRAME mavFrame = followTerrain() || !_cameraCalc.distanceToSurfaceRelative() ? MAV_FRAME_GLOBAL : MAV_FRAME_GLOBAL_RELATIVE_ALT; + + for (const QList& transect: _transects) { + //bool transectEntry = true; + + for (const CoordInfo_t& transectCoordInfo: transect) { + item = new MissionItem(seqNum++, + MAV_CMD_NAV_WAYPOINT, + mavFrame, + 0, // Hold time (delay for hover and capture to settle vehicle before image is taken) + 0.0, // No acceptance radius specified + 0.0, // Pass through waypoint + std::numeric_limits::quiet_NaN(), // Yaw unchanged + transectCoordInfo.coord.latitude(), + transectCoordInfo.coord.longitude(), + transectCoordInfo.coord.altitude(), + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + // implement capture if desired +// if (hoverAndCaptureEnabled()) { +// item = new MissionItem(seqNum++, +// MAV_CMD_IMAGE_START_CAPTURE, +// MAV_FRAME_MISSION, +// 0, // Reserved (Set to 0) +// 0, // Interval (none) +// 1, // Take 1 photo +// qQNaN(), qQNaN(), qQNaN(), qQNaN(), // param 4-7 reserved +// true, // autoContinue +// false, // isCurrentItem +// missionItemParent); +// items.append(item); +// } + +// if (firstOverallPoint && addTriggerAtBeginning) { +// // Start triggering +// addTriggerAtBeginning = false; +// item = new MissionItem(seqNum++, +// MAV_CMD_DO_SET_CAM_TRIGG_DIST, +// MAV_FRAME_MISSION, +// triggerDistance(), // trigger distance +// 0, // shutter integration (ignore) +// 1, // trigger immediately when starting +// 0, 0, 0, 0, // param 4-7 unused +// true, // autoContinue +// false, // isCurrentItem +// missionItemParent); +// items.append(item); +// } + firstOverallPoint = false; + +// // Possibly add trigger start/stop to survey area entrance/exit +// if (triggerCamera() && !hoverAndCaptureEnabled() && transectCoordInfo.coordType == TransectStyleComplexItem::CoordTypeSurveyEdge) { +// if (transectEntry) { +// // Start of transect, always start triggering. We do this even if we are taking images everywhere. +// // This allows a restart of the mission in mid-air without losing images from the entire mission. +// // At most you may lose part of a transect. +// item = new MissionItem(seqNum++, +// MAV_CMD_DO_SET_CAM_TRIGG_DIST, +// MAV_FRAME_MISSION, +// triggerDistance(), // trigger distance +// 0, // shutter integration (ignore) +// 1, // trigger immediately when starting +// 0, 0, 0, 0, // param 4-7 unused +// true, // autoContinue +// false, // isCurrentItem +// missionItemParent); +// items.append(item); +// transectEntry = false; +// } else if (!imagesEverywhere && !transectEntry){ +// // End of transect, stop triggering +// item = new MissionItem(seqNum++, +// MAV_CMD_DO_SET_CAM_TRIGG_DIST, +// MAV_FRAME_MISSION, +// 0, // stop triggering +// 0, // shutter integration (ignore) +// 0, // trigger immediately when starting +// 0, 0, 0, 0, // param 4-7 unused +// true, // autoContinue +// false, // isCurrentItem +// missionItemParent); +// items.append(item); +// } +// } + } + } + + // implemetn photo capture if desired +// if (triggerCamera() && !hoverAndCaptureEnabled() && imagesEverywhere) { +// // Stop triggering +// MissionItem* item = new MissionItem(seqNum++, +// MAV_CMD_DO_SET_CAM_TRIGG_DIST, +// MAV_FRAME_MISSION, +// 0, // stop triggering +// 0, // shutter integration (ignore) +// 0, // trigger immediately when starting +// 0, 0, 0, 0, // param 4-7 unused +// true, // autoContinue +// false, // isCurrentItem +// missionItemParent); +// items.append(item); +// } +} +void CircularSurveyComplexItem::applyNewAltitude(double newAltitude) +{ + _cameraCalc.valueSetIsDistance()->setRawValue(true); + _cameraCalc.distanceToSurface()->setRawValue(newAltitude); + _cameraCalc.setDistanceToSurfaceRelative(true); } double CircularSurveyComplexItem::timeBetweenShots() @@ -94,7 +323,7 @@ double CircularSurveyComplexItem::timeBetweenShots() bool CircularSurveyComplexItem::readyForSave() const { - return false; + return TransectStyleComplexItem::readyForSave(); } double CircularSurveyComplexItem::additionalTimeDelay() const @@ -301,12 +530,17 @@ void CircularSurveyComplexItem::_rebuildTransectsPhase1() void CircularSurveyComplexItem::_recalcComplexDistance() { - + _complexDistance = 0; + for (int i=0; i<_visualTransectPoints.count() - 1; i++) { + _complexDistance += _visualTransectPoints[i].value().distanceTo(_visualTransectPoints[i+1].value()); + } + emit complexDistanceChanged(); } +// no cameraShots in Circular Survey, add if desired void CircularSurveyComplexItem::_recalcCameraShots() { - + _cameraShots = 0; } void CircularSurveyComplexItem::_updateItem() diff --git a/src/Wima/CircularSurveyComplexItem.h b/src/Wima/CircularSurveyComplexItem.h index b4968b9edc20e0ec30a557a53a65c942bf5f9156..47ff384e817db9bc9dea40b5bc18c6402ba27a68 100644 --- a/src/Wima/CircularSurveyComplexItem.h +++ b/src/Wima/CircularSurveyComplexItem.h @@ -56,6 +56,13 @@ public: static const char* deltaRName; static const char* deltaAlphaName; + static const char* jsonComplexItemTypeValue; + static const char* jsonDeltaRKey; + static const char* jsonDeltaAlphaKey; + static const char* jsonReferencePointLongKey; + static const char* jsonReferencePointLatKey; + static const char* jsonReferencePointAltKey; + signals: void refPointChanged(); void autoGeneratedChanged(); @@ -70,6 +77,11 @@ private slots: signals: private: + void _appendLoadedMissionItems(QList& items, QObject* missionItemParent); + void _buildAndAppendMissionItems(QList& items, QObject* missionItemParent); + + + QGeoCoordinate _referencePoint; // center of the circular lanes, e.g. base station QMap _metaDataMap; diff --git a/src/Wima/WimaPlaner.cc b/src/Wima/WimaPlaner.cc index 349f809a75f89e3286da2cde082c0547794d43da..5589c7a692dd5023974723035d39e857b4013c39 100644 --- a/src/Wima/WimaPlaner.cc +++ b/src/Wima/WimaPlaner.cc @@ -497,9 +497,17 @@ bool WimaPlaner::loadFromFile(const QString &filename) emit currentFileChanged(); //recalcJoinedArea(); - // MissionItems4 + // MissionItems // extrac MissionItems part - QJsonDocument missionJsonDoc = QJsonDocument(json[missionItemsName].toObject()); + +// bool ret = json.contains(missionItemsName); +// qWarning() << ret; + + QJsonObject missionObject = json[missionItemsName].toObject(); + + //qWarning() << json[missionItemsName].type(); + + QJsonDocument missionJsonDoc = QJsonDocument(missionObject); // create temporary file with missionItems QFile temporaryFile; QString cropedFileName = filename.section("/",0,-2); @@ -508,7 +516,8 @@ bool WimaPlaner::loadFromFile(const QString &filename) #endif QString temporaryFileName; for (int i = 0; ; i++) { - temporaryFileName = cropedFileName.append("/temp%1.%2").arg(i).arg(AppSettings::planFileExtension); + temporaryFileName = cropedFileName + QString("/temp%1.%2").arg(i).arg(AppSettings::planFileExtension); + // qWarning() << temporaryFileName; if ( !QFile::exists(temporaryFileName) ) { temporaryFile.setFileName(temporaryFileName); @@ -523,7 +532,9 @@ bool WimaPlaner::loadFromFile(const QString &filename) } } + // qWarning() << missionJsonDoc.toVariant().toString(); temporaryFile.write(missionJsonDoc.toJson()); + temporaryFile.close(); // load from temporary file _masterController->loadFromFile(temporaryFileName);