From 8250ef7b16525f6a6353ec5c9cf0eebadb238a8f Mon Sep 17 00:00:00 2001 From: Valentin Platzgummer Date: Thu, 10 Dec 2020 16:02:18 +0100 Subject: [PATCH] measurement complex item loading almost finished --- src/MeasurementComplexItem/AreaData.cc | 13 +- .../CircularGenerator.cpp | 22 +- src/MeasurementComplexItem/GeneratorBase.cc | 20 +- src/MeasurementComplexItem/GeneratorBase.h | 16 +- .../LinearGenerator.cpp | 20 +- .../MeasurementComplexItem.cc | 227 +++++++++++++++--- .../MeasurementComplexItem.h | 15 +- src/MeasurementComplexItem/call_once.h | 7 +- 8 files changed, 280 insertions(+), 60 deletions(-) diff --git a/src/MeasurementComplexItem/AreaData.cc b/src/MeasurementComplexItem/AreaData.cc index 6d612ecc6..ab5efd2a1 100644 --- a/src/MeasurementComplexItem/AreaData.cc +++ b/src/MeasurementComplexItem/AreaData.cc @@ -304,14 +304,16 @@ bool AreaData::load(const QJsonObject &obj, QString &errorString) { if (JsonHelper::validateKeys(obj, keyInfo, e)) { this->clear(); // iterate over json array - for (const auto &jsonArea : obj[areaListKey].toArray()) { + for (const auto valueRef : obj[areaListKey].toArray()) { + const auto jsonArea = valueRef.toObject(); // check if area type key is present QList areaInfo = { {GeoArea::areaTypeKey, QJsonValue::String, true}, }; if (!JsonHelper::validateKeys(jsonArea, areaInfo, e)) { // load MeasurementArea - if (jsonArea[GeoArea::areaTypeKey] == MeasurementArea::name) { + if (jsonArea[GeoArea::areaTypeKey].toString() == + MeasurementArea::name) { auto area = getGeoArea(_areaList); if (area == nullptr) { auto area = new MeasurementArea(this); @@ -330,7 +332,8 @@ bool AreaData::load(const QJsonObject &obj, QString &errorString) { } } // load SafeArea - else if (jsonArea[GeoArea::areaTypeKey] == SafeArea::name) { + else if (jsonArea[GeoArea::areaTypeKey].toString() == + SafeArea::name) { auto area = getGeoArea(_areaList); if (area == nullptr) { auto area = new SafeArea(this); @@ -352,7 +355,7 @@ bool AreaData::load(const QJsonObject &obj, QString &errorString) { else { returnValue = false; errorString.append(tr("Unknown area type: ") + - jsonArea[GeoArea::areaTypeKey]); + jsonArea[GeoArea::areaTypeKey].toString()); } } // GeoArea::areaTypeKey missing @@ -397,7 +400,7 @@ bool AreaData::save(QJsonObject &obj) { QJsonObject temp; QJsonValue jsonOrigin; - JsonHelper::saveGeoCoordinate(_origin, true, jsonOrigin); + JsonHelper::saveGeoCoordinate(_origin, false, jsonOrigin); temp[originKey] = jsonOrigin; temp[initializedKey] = _initialized; diff --git a/src/MeasurementComplexItem/CircularGenerator.cpp b/src/MeasurementComplexItem/CircularGenerator.cpp index e36852f43..aa3b6e7bd 100644 --- a/src/MeasurementComplexItem/CircularGenerator.cpp +++ b/src/MeasurementComplexItem/CircularGenerator.cpp @@ -19,12 +19,20 @@ template <> inline auto get<0>(const IntPoint &p) { return p.X; } template <> inline auto get<1>(const IntPoint &p) { return p.Y; } namespace routing { + +namespace { const QString generatorType = "CircularGenerator"; GeneratorBase *creator(QObject *parent) { return new CircularGenerator(parent); } +const char *distanceKey = "TransectDistance"; +const char *deltaAlphaKey = "DeltaAlpha"; +const char *minLengthKey = "MinLength"; +const char *referenceKey = "ReferencePoint"; +} // namespace + REGISTER_GENERATOR(generatorType, creator) bool circularTransects(const snake::FPoint &reference, @@ -34,10 +42,6 @@ bool circularTransects(const snake::FPoint &reference, snake::Length minLength, snake::Transects &transects); const char *CircularGenerator::settingsGroup = "CircularGenerator"; -const char *distanceKey = "TransectDistance"; -const char *deltaAlphaKey = "DeltaAlpha"; -const char *minLengthKey = "MinLength"; -const char *referenceKey = "ReferencePoint"; CircularGenerator::CircularGenerator(QObject *parent) : CircularGenerator(nullptr, parent) {} @@ -192,6 +196,8 @@ void CircularGenerator::setReference(const QGeoCoordinate &reference) { bool CircularGenerator::save(QJsonObject &obj) const { QJsonObject temp; + GeneratorBase::save(temp); + bool ok = false; auto variant = _distance.rawValue(); auto val = variant.toDouble(&ok); @@ -237,6 +243,14 @@ bool CircularGenerator::save(QJsonObject &obj) const { bool CircularGenerator::load(const QJsonObject &obj, QString &errorString) { bool returnValue = true; + { + QString e; + if (!GeneratorBase::load(obj, e)) { + returnValue = false; + errorString.append(e); + } + } + // load distance { QString e; diff --git a/src/MeasurementComplexItem/GeneratorBase.cc b/src/MeasurementComplexItem/GeneratorBase.cc index 70381e534..52113cea1 100644 --- a/src/MeasurementComplexItem/GeneratorBase.cc +++ b/src/MeasurementComplexItem/GeneratorBase.cc @@ -5,6 +5,7 @@ namespace routing { const char *GeneratorBase::typeKey = "GeneratorType"; +const char *GeneratorBase::nameKey = "Name"; GeneratorBase::GeneratorBase(QObject *parent) : GeneratorBase(nullptr, parent) {} @@ -16,7 +17,24 @@ GeneratorBase::GeneratorBase(GeneratorBase::Data d, QObject *parent) GeneratorBase::~GeneratorBase() {} -QString GeneratorBase::name() { return _name; } +bool GeneratorBase::save(QJsonObject &obj) const { + obj[typeKey] = type(); + obj[name] = name(); + return true; +} + +bool GeneratorBase::load(const QJsonObject &obj, QString &errorString) { + if (obj.contains[nameKey] && obj[nameKey].isString()) { + setName(obj[nameKey].toString()); + return true; + } else { + errorString.append( + tr("Not able to load generator name. Leaving name unchanged.")); + return false; + } +} + +QString GeneratorBase::name() const { return _name; } void GeneratorBase::setName(const QString &name) { if (_name != name) { diff --git a/src/MeasurementComplexItem/GeneratorBase.h b/src/MeasurementComplexItem/GeneratorBase.h index c3900d555..e5b3373f7 100644 --- a/src/MeasurementComplexItem/GeneratorBase.h +++ b/src/MeasurementComplexItem/GeneratorBase.h @@ -27,16 +27,17 @@ public: Q_PROPERTY(QString mapVisualQml READ mapVisualQml CONSTANT) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - virtual QString editorQml() = 0; - virtual QString mapVisualQml() = 0; + virtual QString editorQml() const = 0; + virtual QString mapVisualQml() const = 0; - virtual bool save(QJsonObject &obj) const = 0; - virtual bool load(const QJsonObject &obj, QString &errorString) = 0; + virtual bool save(QJsonObject &obj) const; // must be called if overridden + virtual bool load(const QJsonObject &obj, + QString &errorString); // must be called if overridden - QString name(); + QString name() const; void setName(const QString &name); - virtual QString abbreviation() = 0; - virtual QString type() = 0; + virtual QString abbreviation() const = 0; + virtual QString type() const = 0; virtual bool get(Generator &generator) = 0; @@ -44,6 +45,7 @@ public: void setData(Data d); static const char *typeKey; + static const char *nameKey; signals: void generatorChanged(); diff --git a/src/MeasurementComplexItem/LinearGenerator.cpp b/src/MeasurementComplexItem/LinearGenerator.cpp index 76d5b8e23..f97497936 100644 --- a/src/MeasurementComplexItem/LinearGenerator.cpp +++ b/src/MeasurementComplexItem/LinearGenerator.cpp @@ -12,10 +12,17 @@ #include "nemo_interface/SnakeTile.h" namespace routing { + +namespace { const QString generatorType = "LinearGenerator"; GeneratorBase *creator(QObject *parent) { return new LinearGenerator(parent); } +const char *distanceKey = "TransectDistance"; +const char *alphaKey = "Alpha"; +const char *minLengthKey = "MinLength"; +} // namespace + REGISTER_GENERATOR(generatorType, creator) QGC_LOGGING_CATEGORY(LinearGeneratorLog, "LinearGeneratorLog") @@ -25,9 +32,6 @@ bool linearTransects(const snake::FPolygon &polygon, snake::Length minLength, snake::Transects &transects); const char *LinearGenerator::settingsGroup = "LinearGenerator"; -const char *distanceKey = "TransectDistance"; -const char *alphaKey = "Alpha"; -const char *minLengthKey = "MinLength"; LinearGenerator::LinearGenerator(QObject *parent) : LinearGenerator(nullptr, parent) {} @@ -159,6 +163,8 @@ bool LinearGenerator::get(Generator &generator) { bool LinearGenerator::save(QJsonObject &obj) const { QJsonObject temp; + GeneratorBase::save(temp); + bool ok = false; auto variant = _distance.rawValue(); auto val = variant.toDouble(&ok); @@ -200,6 +206,14 @@ bool LinearGenerator::save(QJsonObject &obj) const { bool LinearGenerator::load(const QJsonObject &obj, QString &errorString) { bool returnValue = true; + { + QString e; + if (!GeneratorBase::load(obj, e)) { + returnValue = false; + errorString.append(e); + } + } + // load distance { QString e; diff --git a/src/MeasurementComplexItem/MeasurementComplexItem.cc b/src/MeasurementComplexItem/MeasurementComplexItem.cc index 2a00c4206..607e26d2c 100644 --- a/src/MeasurementComplexItem/MeasurementComplexItem.cc +++ b/src/MeasurementComplexItem/MeasurementComplexItem.cc @@ -30,13 +30,13 @@ constexpr typename std::underlying_type::type integral(T value) { const char *MeasurementComplexItem::settingsGroup = "MeasurementComplexItem"; const char *MeasurementComplexItem::jsonComplexItemTypeValue = "MeasurementComplexItem"; -const char *MeasurementComplexItem::variantName = "Variant"; -const char *MeasurementComplexItem::altitudeName = "Altitude"; const QString MeasurementComplexItem::name(tr("Measurement")); -const char *areaDataName = "AreaData"; -const char *variantNamesName = "VariantNames"; -const char *generatorsName = "Generators"; -const char *variantsName = "Variants"; +const char *variantKey = "Variant"; +const char *altitudeKey = "Altitude"; +const char *areaDataKey = "AreaData"; +const char *variantNamesKey = "VariantNames"; +const char *generatorsKey = "Generators"; +const char *variantsKey = "Variants"; MeasurementComplexItem::MeasurementComplexItem( PlanMasterController *masterController, bool flyView, @@ -46,8 +46,8 @@ MeasurementComplexItem::MeasurementComplexItem( _metaDataMap(FactMetaData::createMapFromJsonFile( QStringLiteral(":/json/MeasurementComplexItem.SettingsGroup.json"), this)), - _altitude(settingsGroup, _metaDataMap[altitudeName]), - _variant(settingsGroup, _metaDataMap[variantName]), + _altitude(settingsGroup, _metaDataMap[altitudeKey]), + _variant(settingsGroup, _metaDataMap[variantKey]), _pAreaData(new AreaData(this)), _pEditorData(new AreaData(this)), _pCurrentData(_pAreaData), _pGenerator(nullptr), _pWorker(new RoutingThread(this)) { @@ -103,6 +103,8 @@ MeasurementComplexItem::MeasurementComplexItem( resetGenerators(); qCritical() << "ToDo: _altitude connections missing."; + qCritical() << "ToDo: remove generatorNameList and use GeneratorBase::name() " + "instead."; } MeasurementComplexItem::~MeasurementComplexItem() {} @@ -123,10 +125,6 @@ QStringList MeasurementComplexItem::variantNames() const { bool MeasurementComplexItem::load(const QJsonObject &complexObject, int sequenceNumber, QString &errorString) { - qWarning() << "MeasurementComplexItem::load(): area data load missing."; - qWarning() << "MeasurementComplexItem::load(): mission item load missing."; - qWarning() << "MeasurementComplexItem::load(): add editingStart/Stop."; - // We need to pull version first to determine what validation/conversion // needs to be performed QList versionKeyInfoList = { @@ -169,23 +167,166 @@ bool MeasurementComplexItem::load(const QJsonObject &complexObject, setSequenceNumber(sequenceNumber); + startEditing(); + bool returnValue = true; + // load variant - if (complexObject.contains(variantName) && - complexObject[variantName].isDouble()) { - _variant.setRawValue(complexObject[variantName].toInt()); + if (complexObject.contains(variantKey) && + complexObject[variantKey].isDouble()) { + _variant.setRawValue(complexObject[variantKey].toInt()); } else { - errorString.append(tr("Not able to load route variant number.")); + returnValue = false; + errorString.append(tr("No route variant number found in file.\n")); } // load altitude - if (complexObject.contains(altitudeName) && - complexObject[altitudeName].isDouble()) { - _altitude.setRawValue(complexObject[altitudeName].toDouble()); + if (complexObject.contains(altitudeKey) && + complexObject[altitudeKey].isDouble()) { + _altitude.setRawValue(complexObject[altitudeKey].toDouble()); + } else { + returnValue = false; + errorString.append(tr("No altitude found in file.\n")); + } + + // load AreaData. + if (complexObject.contains(areaDataKey) && + complexObject[areaDataKey].isObject()) { + QString e; + if (_pCurrentData->load(complexObject[areaDataKey].toObject(), e)) { + _pCurrentData->setShowErrorMessages(false); + if (!_pCurrentData->isCorrect()) { + _pCurrentData->setShowErrorMessages(true); + errorString.append( + tr("Loaded area data does not fullfill rules. Abort loading.\n")); + abortEditing(); + return false; + } + _pCurrentData->setShowErrorMessages(true); + } else { + // this is critical, proceeding is not + // reasonable. + errorString.append(e); + errorString.append(tr("Abort loading.\n")); + abortEditing(); + return false; + } + } else { + // this is critical, if no area data present, proceeding is not reasonable. + errorString.append(tr("No area data found in file. Abort loading.\n")); + abortEditing(); + return false; + } + + // load Generators. + if (complexObject.contains(generatorsKey) && + complexObject[generatorsKey].isArray()) { + + QVector generatorList; + QObject parent; + + for (const auto valueRef : complexObject[generatorsKey].toArray()) { + const auto jsonGen = valueRef.toObject(); + + if (jsonGen.contains(routing::GeneratorBase::typeKey) && + jsonGen[routing::GeneratorBase::typeKey].isString()) { + QString e; + + // create generator + auto gen = routing::GeneratorFactory::instance()->create( + jsonGen, e, &parent /*parent*/); + + if (gen != nullptr) { + // remove generators of same type and insert this generator. + for (int i = 0; i < _generatorList.size();) { + auto otherGen = generator(i); + if (gen->type() == otherGen->type()) { + removeGenerator(i); + } else { + ++i; + } + } + gen->setData(this->_pAreaData); + generatorList.append(gen); + } else { + // error loading generator. + errorString.append( + tr("Error loading generator of type ") + + jsonGen[routing::GeneratorBase::typeKey].toString() + ".\n"); + if (!routing::GeneratorFactory::instance()->registered( + jsonGen[routing::GeneratorBase::typeKey].toString())) { + errorString.append(tr("This type is unknown.\n")); + qCritical() + << "MeasurementComplexItem::load(): generator of type :" + << jsonGen[routing::GeneratorBase::typeKey] + << " not registered with the GeneratorFactory. This can either " + "mean that the file contains a invalid entry or " + "that the generator was not registered. In the latter case " + "use the REGISTER_GENERATOR() for registration"; + } + returnValue = false; + } + + } else { + errorString.append(tr( + "Can not determine type of generator. Skipping this generator.")); + returnValue = false; + } + + // insert generators + for (const auto gen : generatorList) { + gen->setParent(this); + addGenerator(gen->name(), gen); + } + } + } else { + returnValue = false; + errorString.append( + tr("No generators found in file. Leaving generators unchanged.\n")); + } + + // load Route Variants + if (complexObject.contains(variantsKey) && + complexObject[variantsKey].isArray()) { + + QVector variantVector; + + // load variants to variantVector for further processing. + for (const auto valueRef : complexObject[variantsKey].toArray()) { + if (valueRef.isArray()) { + const auto jsonVariant = valueRef.toObject(); + Variant variant; + QString e; + if (JsonHelper::loadGeoCoordinateArray(jsonVariant, false, variantList, + e)) { + if (variant.size() > 0) { + variantVector.append(std::move(variant)); + } else { + errorString.append("Empty route variant skipped.\n"); + returnValue = false; + } + } else { + errorString.append(e); + errorString.append("\n"); + returnValue = false; + } + } else { + qCDebug(MeasurementComplexItemLog) + << "json variant is not an array but of type: " << valueRef.type(); + returnValue = false; + } + } + + // Check if variants are covered by safe area. + qCritical() << "Continue here!"; } else { - errorString.append(tr("Not able to load altitude.")); + returnValue = false; + errorString.append(tr( + "No route variants found in file. Calculating new route variants.\n")); + stopEditing(); } - return true; + stopEditing(false); + return returnValue; } double @@ -227,15 +368,15 @@ void MeasurementComplexItem::save(QJsonArray &planItems) { jsonComplexItemTypeValue; // Variant and altitude. - saveObject[variantName] = double(_variant.rawValue().toUInt()); - saveObject[altitudeName] = double(_altitude.rawValue().toUInt()); + saveObject[variantKey] = double(_variant.rawValue().toUInt()); + saveObject[altitudeKey] = double(_altitude.rawValue().toUInt()); // Variant names. QJsonArray jsonVariantNames; for (auto const &name : _variantNames) { jsonVariantNames.append(name); } - saveObject[variantNamesName] = jsonVariantNames; + saveObject[variantNamesKey] = jsonVariantNames; // AreaData. QJsonObject jsonAreaData; @@ -244,7 +385,7 @@ void MeasurementComplexItem::save(QJsonArray &planItems) { << "save(): not able to save area data"; return; } - saveObject[areaDataName] = jsonAreaData; + saveObject[areaDataKey] = jsonAreaData; // Generators. QJsonArray generatorArray; @@ -262,7 +403,7 @@ void MeasurementComplexItem::save(QJsonArray &planItems) { generatorArray.append(outerObj); } } - saveObject[generatorsName] = generatorArray; + saveObject[generatorsKey] = generatorArray; // Route Variants QJsonArray variantsArray; @@ -275,7 +416,7 @@ void MeasurementComplexItem::save(QJsonArray &planItems) { } variantsArray.append(variant); } - saveObject[variantsName] = variantsArray; + saveObject[variantsKey] = variantsArray; planItems.append(saveObject); } else { @@ -777,7 +918,7 @@ void MeasurementComplexItem::resetGenerators() { addGenerator(cg->name(), cg); } -QStringList MeasurementComplexItem::generatorNameList() { +QStringList MeasurementComplexItem::generatorNameList() const { return this->_generatorNameList; } @@ -785,7 +926,28 @@ routing::GeneratorBase *MeasurementComplexItem::generator() { return _pGenerator; } -int MeasurementComplexItem::generatorIndex() { +const routing::GeneratorBase *MeasurementComplexItem::generator() const { + return _pGenerator; +} + +const routing::GeneratorBase * +MeasurementComplexItem::generator(int index) const { + if (index > 0 && index < _generatorList.size()) { + return _generatorList[index]; + } else { + return nullptr; + } +} + +routing::GeneratorBase *MeasurementComplexItem::generator(int index) { + if (index > 0 && index < _generatorList.size()) { + return _generatorList[index]; + } else { + return nullptr; + } +} + +int MeasurementComplexItem::generatorIndex() const { return this->_generatorList.indexOf(this->_pGenerator); } @@ -797,7 +959,7 @@ void MeasurementComplexItem::startEditing() { } } -void MeasurementComplexItem::stopEditing() { +bool MeasurementComplexItem::stopEditing(bool doUpdate) { if (editing()) { bool correct = _pEditorData->isCorrect(); if (correct) { @@ -805,10 +967,13 @@ void MeasurementComplexItem::stopEditing() { } _setAreaData(_pAreaData); _setState(STATE::IDLE); - if (correct && *_pEditorData != *_pAreaData) { + if (doUpdate && correct && *_pEditorData != *_pAreaData) { _updateRoute(); } + + return correct; } + return false; } void MeasurementComplexItem::abortEditing() { diff --git a/src/MeasurementComplexItem/MeasurementComplexItem.h b/src/MeasurementComplexItem/MeasurementComplexItem.h index 3afacf3e2..fe0dcea63 100644 --- a/src/MeasurementComplexItem/MeasurementComplexItem.h +++ b/src/MeasurementComplexItem/MeasurementComplexItem.h @@ -101,9 +101,12 @@ public: Q_INVOKABLE bool switchToGenerator(const QString &name); Q_INVOKABLE bool switchToGenerator(int index); Q_INVOKABLE void resetGenerators(); - QStringList generatorNameList(); + QStringList generatorNameList() const; routing::GeneratorBase *generator(); - int generatorIndex(); + const routing::GeneratorBase *generator() const; + routing::GeneratorBase *generator(int index); + const routing::GeneratorBase *generator(int index) const; + int generatorIndex() const; // Editing. //! @@ -119,7 +122,11 @@ public: //! Stops area editing. Will reset area data to the state before //! editingStart() if it is invalid. Triggers a route update. //! - Q_INVOKABLE void stopEditing(); + //! \param doUpdate No route update will be triggered if false, route update + //! will eventually be triggered if true. \return Returns true if a route + //! update was triggered, false either. + //! + Q_INVOKABLE bool stopEditing(bool doUpdate = true); //! //! \brief abortEditing Aborts area editing. //! @@ -143,8 +150,6 @@ public: bool followTerrain() const; static const char *settingsGroup; - static const char *variantName; - static const char *altitudeName; static const char *jsonComplexItemTypeValue; static const QString name; diff --git a/src/MeasurementComplexItem/call_once.h b/src/MeasurementComplexItem/call_once.h index 966f2afa2..b5f4574a6 100644 --- a/src/MeasurementComplexItem/call_once.h +++ b/src/MeasurementComplexItem/call_once.h @@ -38,10 +38,9 @@ inline static void qCallOnce(Function func, QBasicAtomicInt &flag) { } template inline static void qCallOncePerThread(Function func) { - using namespace CallOnce; - if (!once_flag()->hasLocalData()) { - once_flag()->setLocalData(new QAtomicInt(CO_Request)); - qCallOnce(func, *once_flag()->localData()); + if (!CallOnce::once_flag()->hasLocalData()) { + CallOnce::once_flag()->setLocalData(new QAtomicInt(CallOnce::CO_Request)); + qCallOnce(func, *CallOnce::once_flag()->localData()); } } -- 2.22.0