diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 496399d98141ec832b5f88cab4fe05b24cb88542..3c76c8158ae8ce6b3996515fdbcb9a326f6141db 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -204,7 +204,7 @@ void MissionController::loadFromVehicle(void) void MissionController::sendToVehicle(void) { sendItemsToVehicle(_activeVehicle, _visualItems); - _visualItems->setDirty(false); + setDirty(false); } /// Converts from visual items to MissionItems @@ -322,7 +322,7 @@ void MissionController::removeMissionItem(int index) item->deleteLater(); _recalcAll(); - _visualItems->setDirty(true); + setDirty(true); } void MissionController::removeAll(void) @@ -334,7 +334,7 @@ void MissionController::removeAll(void) _visualItems = new QmlObjectListModel(this); _addMissionSettings(_activeVehicle, _visualItems, false /* addToCenter */); _initAllVisualItems(); - _visualItems->setDirty(true); + setDirty(true); _resetMissionFlightStatus(); } } @@ -579,7 +579,6 @@ bool MissionController::_loadJsonMissionFileV2(Vehicle* vehicle, const QJsonObje return true; } -#if 0 bool MissionController::_loadItemsFromJson(const QJsonObject& json, QmlObjectListModel* visualItems, QString& errorString) { // V1 file format has no file type key and version key is string. Convert to new format. @@ -588,14 +587,12 @@ bool MissionController::_loadItemsFromJson(const QJsonObject& json, QmlObjectLis } int fileVersion; - if (!JsonHelper::validateQGCJsonFile(json, - _jsonFileTypeValue, // expected file type - 1, // minimum supported version - 2, // maximum supported version - fileVersion, - errorString)) { - return false; - } + JsonHelper::validateQGCJsonFile(json, + _jsonFileTypeValue, // expected file type + 1, // minimum supported version + 2, // maximum supported version + fileVersion, + errorString); if (fileVersion == 1) { return _loadJsonMissionFileV1(_activeVehicle, json, visualItems, errorString); @@ -603,11 +600,11 @@ bool MissionController::_loadItemsFromJson(const QJsonObject& json, QmlObjectLis return _loadJsonMissionFileV2(_activeVehicle, json, visualItems, errorString); } } -#endif bool MissionController::_loadTextMissionFile(Vehicle* vehicle, QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString) { - bool addPlannedHomePosition = false; + bool firstItem = true; + bool plannedHomePositionInFile = false; QString firstLine = stream.readLine(); const QStringList& version = firstLine.split(" "); @@ -617,19 +614,29 @@ bool MissionController::_loadTextMissionFile(Vehicle* vehicle, QTextStream& stre if (version[2] == "110") { // ArduPilot file, planned home position is already in position 0 versionOk = true; + plannedHomePositionInFile = true; } else if (version[2] == "120") { // Old QGC file, no planned home position versionOk = true; - addPlannedHomePosition = true; + plannedHomePositionInFile = false; } } if (versionOk) { + // Start with planned home in center + _addMissionSettings(vehicle, visualItems, true /* addToCenter */); + MissionSettingsItem* settingsItem = visualItems->value(0); + while (!stream.atEnd()) { SimpleMissionItem* item = new SimpleMissionItem(vehicle, visualItems); if (item->load(stream)) { - visualItems->append(item); + if (firstItem && plannedHomePositionInFile) { + settingsItem->setCoordinate(item->coordinate()); + } else { + visualItems->append(item); + } + firstItem = false; } else { errorString = QStringLiteral("The mission file is corrupted."); return false; @@ -640,9 +647,7 @@ bool MissionController::_loadTextMissionFile(Vehicle* vehicle, QTextStream& stre return false; } - if (addPlannedHomePosition || visualItems->count() == 0) { - _addMissionSettings(vehicle, visualItems, true /* addToCenter */); - + if (!plannedHomePositionInFile) { // Update sequence numbers in DO_JUMP commands to take into account added home position in index 0 for (int i=1; icount(); i++) { SimpleMissionItem* item = qobject_cast(visualItems->get(i)); @@ -655,24 +660,15 @@ bool MissionController::_loadTextMissionFile(Vehicle* vehicle, QTextStream& stre return true; } -bool MissionController::load(const QJsonObject& json, QString& errorString) +void MissionController::_initLoadedVisualItems(QmlObjectListModel* loadedVisualItems) { - QString errorStr; - QString errorMessage = tr("Mission: %1"); - QmlObjectListModel* newVisualItems = new QmlObjectListModel(this); - - if (!_loadJsonMissionFileV2(_activeVehicle, json, newVisualItems, errorStr)) { - errorString = errorMessage.arg(errorStr); - return false; - } - if (_visualItems) { _deinitAllVisualItems(); _visualItems->deleteLater(); _settingsItem = NULL; } - _visualItems = newVisualItems; + _visualItems = loadedVisualItems; if (_visualItems->count() == 0) { _addMissionSettings(_activeVehicle, _visualItems, true /* addToCenter */); @@ -681,12 +677,62 @@ bool MissionController::load(const QJsonObject& json, QString& errorString) MissionController::_scanForAdditionalSettings(_visualItems, _activeVehicle); _initAllVisualItems(); +} - if (!_activeVehicle->isOfflineEditingVehicle()) { - // Needs a sync to vehicle - setDirty(true); +bool MissionController::load(const QJsonObject& json, QString& errorString) +{ + QString errorStr; + QString errorMessage = tr("Mission: %1"); + QmlObjectListModel* loadedVisualItems = new QmlObjectListModel(this); + + if (!_loadJsonMissionFileV2(_activeVehicle, json, loadedVisualItems, errorStr)) { + errorString = errorMessage.arg(errorStr); + return false; + } + _initLoadedVisualItems(loadedVisualItems); + + return true; +} + +bool MissionController::loadJsonFile(QFile& file, QString& errorString) +{ + QString errorStr; + QString errorMessage = tr("Mission: %1"); + QJsonDocument jsonDoc; + QByteArray bytes = file.readAll(); + + if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorStr)) { + errorString = errorMessage.arg(errorStr); + return false; + } + + QJsonObject json = jsonDoc.object(); + QmlObjectListModel* loadedVisualItems = new QmlObjectListModel(this); + if (!_loadItemsFromJson(json, loadedVisualItems, errorStr)) { + errorString = errorMessage.arg(errorStr); + return false; } + _initLoadedVisualItems(loadedVisualItems); + + return true; +} + +bool MissionController::loadTextFile(QFile& file, QString& errorString) +{ + QString errorStr; + QString errorMessage = tr("Mission: %1"); + QByteArray bytes = file.readAll(); + QTextStream stream(bytes); + + QmlObjectListModel* loadedVisualItems = new QmlObjectListModel(this); + if (!_loadTextMissionFile(_activeVehicle, stream, loadedVisualItems, errorStr)) { + errorString = errorMessage.arg(errorStr); + return false; + } + + _initLoadedVisualItems(loadedVisualItems); + return true; } @@ -1196,7 +1242,7 @@ void MissionController::_initAllVisualItems(void) emit containsItemsChanged(containsItems()); emit plannedHomePositionChanged(plannedHomePosition()); - _visualItems->setDirty(false); + setDirty(false); } void MissionController::_deinitAllVisualItems(void) @@ -1214,7 +1260,7 @@ void MissionController::_deinitAllVisualItems(void) void MissionController::_initVisualItem(VisualMissionItem* visualItem) { - _visualItems->setDirty(false); + setDirty(false); connect(visualItem, &VisualMissionItem::specifiesCoordinateChanged, this, &MissionController::_recalcWaypointLines); connect(visualItem, &VisualMissionItem::coordinateHasRelativeAltitudeChanged, this, &MissionController::_recalcWaypointLines); @@ -1270,6 +1316,7 @@ void MissionController::activeVehicleBeingRemoved(void) // We always remove all items on vehicle change. This leaves a user model hole: // If the user has unsaved changes in the Plan view they will lose them removeAll(); + setDirty(false); _activeVehicle = NULL; } @@ -1281,6 +1328,7 @@ void MissionController::activeVehicleSet(Vehicle* activeVehicle) // We always remove all items on vehicle change. This leaves a user model hole: // If the user has unsaved changes in the Plan view they will lose them removeAll(); + setDirty(false); MissionManager* missionManager = _activeVehicle->missionManager(); diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index 4e734574050df61b5bb191de8e6d5264dcfb705b..100b51b8b39538844ad4266e5f06243f9b247910 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -103,6 +103,9 @@ public: Q_INVOKABLE void clearCameraPoints(void); + bool loadJsonFile(QFile& file, QString& errorString); + bool loadTextFile(QFile& file, QString& errorString); + // Overrides from PlanElementController void start (bool editMode) final; void startStaticActiveVehicle (Vehicle* vehicle) final; @@ -110,9 +113,6 @@ public: bool load (const QJsonObject& json, QString& errorString) final; void loadFromVehicle (void) final; void sendToVehicle (void) final; -#if 0 - void loadFromFile (const QString& filename) final; -#endif void removeAll (void) final; void removeAllFromVehicle (void) final; bool syncInProgress (void) const final; @@ -202,6 +202,7 @@ private: void _addCruiseTime(double cruiseTime, double cruiseDistance, int wayPointIndex); void _updateBatteryInfo(int waypointIndex); bool _loadItemsFromJson(const QJsonObject& json, QmlObjectListModel* visualItems, QString& errorString); + void _initLoadedVisualItems(QmlObjectListModel* loadedVisualItems); private: QmlObjectListModel* _visualItems; diff --git a/src/MissionManager/PlanMasterController.cc b/src/MissionManager/PlanMasterController.cc index bb2bd3eaf6b7d4002676901e5bf2e6d364eab260..5e5d13ab8384b73cedec63da60f5b2c3229fbc11 100644 --- a/src/MissionManager/PlanMasterController.cc +++ b/src/MissionManager/PlanMasterController.cc @@ -123,38 +123,52 @@ void PlanMasterController::loadFromFile(const QString& filename) return; } - QJsonDocument jsonDoc; - QByteArray bytes = file.readAll(); - - if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) { - qgcApp()->showMessage(errorMessage.arg(errorString)); - return; - } - - int version; - QJsonObject json = jsonDoc.object(); - if (!JsonHelper::validateQGCJsonFile(json, _planFileType, _planFileVersion, _planFileVersion, version, errorString)) { - qgcApp()->showMessage(errorMessage.arg(errorString)); - return; + QString fileExtension(".%1"); + if (filename.endsWith(fileExtension.arg(AppSettings::planFileExtension))) { + QJsonDocument jsonDoc; + QByteArray bytes = file.readAll(); + + if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) { + qgcApp()->showMessage(errorMessage.arg(errorString)); + return; + } + + int version; + QJsonObject json = jsonDoc.object(); + if (!JsonHelper::validateQGCJsonFile(json, _planFileType, _planFileVersion, _planFileVersion, version, errorString)) { + qgcApp()->showMessage(errorMessage.arg(errorString)); + return; + } + + QList rgKeyInfo = { + { _jsonMissionObjectKey, QJsonValue::Object, true }, + { _jsonGeoFenceObjectKey, QJsonValue::Object, true }, + { _jsonRallyPointsObjectKey, QJsonValue::Object, true }, + }; + if (!JsonHelper::validateKeys(json, rgKeyInfo, errorString)) { + qgcApp()->showMessage(errorMessage.arg(errorString)); + return; + } + + if (!_missionController.load(json[_jsonMissionObjectKey].toObject(), errorString) || + !_geoFenceController.load(json[_jsonGeoFenceObjectKey].toObject(), errorString) || + !_rallyPointController.load(json[_jsonRallyPointsObjectKey].toObject(), errorString)) { + qgcApp()->showMessage(errorMessage.arg(errorString)); + } + } else if (filename.endsWith(fileExtension.arg(AppSettings::missionFileExtension))) { + if (!_missionController.loadJsonFile(file, errorString)) { + qgcApp()->showMessage(errorMessage.arg(errorString)); + } + } else if (filename.endsWith(fileExtension.arg(AppSettings::waypointsFileExtension)) || + filename.endsWith(fileExtension.arg(QStringLiteral("txt")))) { + if (!_missionController.loadTextFile(file, errorString)) { + qgcApp()->showMessage(errorMessage.arg(errorString)); + } } - QList rgKeyInfo = { - { _jsonMissionObjectKey, QJsonValue::Object, true }, - { _jsonGeoFenceObjectKey, QJsonValue::Object, true }, - { _jsonRallyPointsObjectKey, QJsonValue::Object, true }, - }; - if (!JsonHelper::validateKeys(json, rgKeyInfo, errorString)) { - qgcApp()->showMessage(errorMessage.arg(errorString)); - return; - } - - if (!_missionController.load(json[_jsonMissionObjectKey].toObject(), errorString) || - !_geoFenceController.load(json[_jsonGeoFenceObjectKey].toObject(), errorString) || - !_rallyPointController.load(json[_jsonRallyPointsObjectKey].toObject(), errorString)) { - qgcApp()->showMessage(errorMessage.arg(errorString)); - return; + if (!_activeVehicle->isOfflineEditingVehicle()) { + setDirty(true); } - setDirty(true); } void PlanMasterController::saveToFile(const QString& filename) @@ -239,9 +253,7 @@ QStringList PlanMasterController::loadNameFilters(void) const { QStringList filters; - filters << tr("Plan Files (*.%1)").arg(AppSettings::planFileExtension) << - tr("Mission Files (*.%1)").arg(AppSettings::missionFileExtension) << - tr("Waypoint Files (*.waypoints)") << + filters << tr("Supported types (*.%1 *.%2 *.%3 *.%4)").arg(AppSettings::planFileExtension).arg(AppSettings::missionFileExtension).arg(AppSettings::waypointsFileExtension).arg("*.txt") << tr("All Files (*.*)"); return filters; } diff --git a/src/PlanView/PlanView.qml b/src/PlanView/PlanView.qml index 5e3e41943afd8a12f4ee404abd951da24a3341e0..9f3dacb3863c6b02c75ec8096337d655533ebe50 100644 --- a/src/PlanView/PlanView.qml +++ b/src/PlanView/PlanView.qml @@ -396,9 +396,9 @@ QGCView { color: qgcPal.window title: qsTr("Plan") z: QGroundControl.zOrderWidgets - showAlternateIcon: [ false, false, !_autoSync && masterController.dirty, false, false, false ] + showAlternateIcon: [ false, false, masterController.dirty, false, false, false ] rotateImage: [ false, false, masterController.syncInProgress, false, false, false ] - animateImage: [ false, false, !_autoSync && masterController.dirty, false, false, false ] + animateImage: [ false, false, masterController.dirty, false, false, false ] buttonEnabled: [ true, true, !masterController.syncInProgress, true, true, true ] buttonVisible: [ true, true, true, true, _showZoom, _showZoom ] maxHeight: mapScale.y - toolStrip.y diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index 80f616188a807497003fd2435a31dfd26fc35ea8..18d8e2b48f76f81bfca490ee080945b95bb7d84f 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -36,6 +36,7 @@ const char* AppSettings::automaticMissionUploadName = "Automat const char* AppSettings::parameterFileExtension = "params"; const char* AppSettings::planFileExtension = "plan"; const char* AppSettings::missionFileExtension = "mission"; +const char* AppSettings::waypointsFileExtension = "waypoints"; const char* AppSettings::fenceFileExtension = "fence"; const char* AppSettings::rallyPointFileExtension = "rally"; const char* AppSettings::telemetryFileExtension = "tlog"; diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index bc98a51f05d4fb2427022ceb91303df79c690919..c7933544d50d07ae0a4c20fb6f3e95cdbfc8e062 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -42,6 +42,7 @@ public: Q_PROPERTY(QString planFileExtension MEMBER planFileExtension CONSTANT) Q_PROPERTY(QString missionFileExtension MEMBER missionFileExtension CONSTANT) + Q_PROPERTY(QString waypointsFileExtension MEMBER waypointsFileExtension CONSTANT) Q_PROPERTY(QString parameterFileExtension MEMBER parameterFileExtension CONSTANT) Q_PROPERTY(QString telemetryFileExtension MEMBER telemetryFileExtension CONSTANT) @@ -89,6 +90,7 @@ public: static const char* parameterFileExtension; static const char* planFileExtension; static const char* missionFileExtension; + static const char* waypointsFileExtension; static const char* fenceFileExtension; static const char* rallyPointFileExtension; static const char* telemetryFileExtension;