AirMapRulesetsManager.cc 14 KB
Newer Older
1 2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

10
#include "AirspaceFlightPlanProvider.h"
11
#include "AirMapRulesetsManager.h"
Gus Grubba's avatar
Gus Grubba committed
12
#include "AirMapManager.h"
13
#include "QGCApplication.h"
Gus Grubba's avatar
Gus Grubba committed
14
#include <QSettings>
15

Gus Grubba's avatar
Gus Grubba committed
16 17
using namespace airmap;

Gus Grubba's avatar
Gus Grubba committed
18 19
static const char* kAirMapFeatureGroup = "AirMapFeatureGroup";

20 21 22 23 24 25 26 27 28 29 30
//-----------------------------------------------------------------------------
AirMapRuleFeature::AirMapRuleFeature(QObject* parent)
    : AirspaceRuleFeature(parent)
{
}

//-----------------------------------------------------------------------------
AirMapRuleFeature::AirMapRuleFeature(airmap::RuleSet::Feature feature, QObject* parent)
    : AirspaceRuleFeature(parent)
    , _feature(feature)
{
Gus Grubba's avatar
Gus Grubba committed
31 32 33
    //-- Restore persisted value (if it exists)
    QSettings settings;
    settings.beginGroup(kAirMapFeatureGroup);
34 35
    switch(_feature.type) {
    case RuleSet::Feature::Type::boolean:
36
        //-- For boolean, we have 3 states: 0 - false, 1 - true and 2 - not set
37
        _value = settings.value(name(), 2);
38 39
        break;;
    case RuleSet::Feature::Type::floating_point:
40
        _value = settings.value(name(), NAN);
41 42 43 44 45 46 47
        break;;
    case RuleSet::Feature::Type::string:
        _value = settings.value(name(), QString());
        break;;
    default:
        break;
    }
Gus Grubba's avatar
Gus Grubba committed
48
    settings.endGroup();
49 50
}

51 52 53 54 55 56 57 58
//-----------------------------------------------------------------------------
QVariant
AirMapRuleFeature::value()
{
    //qCDebug(AirMapManagerLog) << "Value of" << name() << "==>" << _value << type();
    return _value;
}

59 60 61 62
//-----------------------------------------------------------------------------
AirspaceRuleFeature::Type
AirMapRuleFeature::type()
{
Gus Grubba's avatar
Gus Grubba committed
63 64 65 66 67 68 69 70 71 72
    switch(_feature.type) {
    case RuleSet::Feature::Type::boolean:
        return AirspaceRuleFeature::Boolean;
    case RuleSet::Feature::Type::floating_point:
        return AirspaceRuleFeature::Float;
    case RuleSet::Feature::Type::string:
        return AirspaceRuleFeature::String;
    default:
        break;
    }
73 74 75 76 77 78 79
    return AirspaceRuleFeature::Unknown;
}

//-----------------------------------------------------------------------------
AirspaceRuleFeature::Unit
AirMapRuleFeature::unit()
{
Gus Grubba's avatar
Gus Grubba committed
80 81 82 83 84 85 86 87 88 89
    switch(_feature.unit) {
    case RuleSet::Feature::Unit::kilograms:
        return AirspaceRuleFeature::Kilogram;
    case RuleSet::Feature::Unit::meters:
        return AirspaceRuleFeature::Meters;
    case RuleSet::Feature::Unit::meters_per_sec:
        return AirspaceRuleFeature::MetersPerSecond;
    default:
        break;
    }
90 91 92 93 94 95 96
    return AirspaceRuleFeature::UnknownUnit;
}

//-----------------------------------------------------------------------------
AirspaceRuleFeature::Measurement
AirMapRuleFeature::measurement()
{
Gus Grubba's avatar
Gus Grubba committed
97 98 99 100 101 102 103 104 105 106
    switch(_feature.measurement) {
    case RuleSet::Feature::Measurement::speed:
        return AirspaceRuleFeature::Speed;
    case RuleSet::Feature::Measurement::weight:
        return AirspaceRuleFeature::Weight;
    case RuleSet::Feature::Measurement::distance:
        return AirspaceRuleFeature::Distance;
    default:
        break;
    }
107 108 109
    return AirspaceRuleFeature::UnknownMeasurement;
}

Gus Grubba's avatar
Gus Grubba committed
110 111 112 113
//-----------------------------------------------------------------------------
void
AirMapRuleFeature::setValue(const QVariant val)
{
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
    switch(_feature.type) {
    case RuleSet::Feature::Type::boolean:
        if(val.toInt() != 0 && val.toInt() != 1) {
            return;
        }
        break;
    case RuleSet::Feature::Type::floating_point:
        if(!std::isfinite(val.toDouble())) {
            return;
        }
        break;;
    case RuleSet::Feature::Type::string:
        if(val.toString().isEmpty()) {
            return;
        }
        break;;
    default:
        return;
    }
Gus Grubba's avatar
Gus Grubba committed
133 134 135 136 137 138 139
    _value = val;
    //-- Make value persistent
    QSettings settings;
    settings.beginGroup(kAirMapFeatureGroup);
    settings.setValue(name(), _value);
    settings.endGroup();
    emit valueChanged();
140
    qgcApp()->toolbox()->airspaceManager()->flightPlan()->setDirty(true);
Gus Grubba's avatar
Gus Grubba committed
141 142
}

Gus Grubba's avatar
Gus Grubba committed
143 144 145
//-----------------------------------------------------------------------------
AirMapRule::AirMapRule(QObject* parent)
    : AirspaceRule(parent)
146 147 148
{
}

149 150 151 152 153 154 155
//-----------------------------------------------------------------------------
AirMapRule::AirMapRule(const airmap::RuleSet::Rule& rule, QObject* parent)
    : AirspaceRule(parent)
    , _rule(rule)
{
}

Gus Grubba's avatar
Gus Grubba committed
156 157 158 159 160 161
//-----------------------------------------------------------------------------
AirMapRule::~AirMapRule()
{
    _features.deleteListAndContents();
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
//-----------------------------------------------------------------------------
AirspaceRule::Status
AirMapRule::status()
{
    switch(_rule.status) {
    case RuleSet::Rule::Status::conflicting:
        return AirspaceRule::Conflicting;
    case RuleSet::Rule::Status::not_conflicting:
        return AirspaceRule::NotConflicting;
    case RuleSet::Rule::Status::missing_info:
        return AirspaceRule::MissingInfo;
    case RuleSet::Rule::Status::unknown:
    default:
        return AirspaceRule::Unknown;
    }
}

//-----------------------------------------------------------------------------
AirMapRuleSet::AirMapRuleSet(QObject* parent)
    : AirspaceRuleSet(parent)
Gus Grubba's avatar
Gus Grubba committed
182
    , _isDefault(false)
Gus Grubba's avatar
Gus Grubba committed
183
    , _selected(false)
184
    , _selectionType(AirspaceRuleSet::Optional)
Gus Grubba's avatar
Gus Grubba committed
185 186 187
{
}

188 189 190 191 192 193
//-----------------------------------------------------------------------------
AirMapRuleSet::~AirMapRuleSet()
{
    _rules.deleteListAndContents();
}

Gus Grubba's avatar
Gus Grubba committed
194 195 196 197 198
//-----------------------------------------------------------------------------
void
AirMapRuleSet::setSelected(bool sel)
{
    if(_selectionType != AirspaceRuleSet::Required) {
199 200 201 202
        if(_selected != sel) {
            _selected = sel;
            emit selectedChanged();
        }
Gus Grubba's avatar
Gus Grubba committed
203
    } else {
204 205 206 207
        if(!_selected) {
            _selected = true;
            emit selectedChanged();
        }
Gus Grubba's avatar
Gus Grubba committed
208 209 210
    }
}

211 212 213 214 215 216
//-----------------------------------------------------------------------------
AirMapRulesetsManager::AirMapRulesetsManager(AirMapSharedState& shared)
    : _shared(shared)
{
}

217 218 219 220 221 222 223
//-----------------------------------------------------------------------------
static bool
rules_sort(QObject* a, QObject* b)
{
    AirMapRule* aa = qobject_cast<AirMapRule*>(a);
    AirMapRule* bb = qobject_cast<AirMapRule*>(b);
    if(!aa || !bb) return false;
224
    return static_cast<int>(aa->order()) > static_cast<int>(bb->order());
225 226
}

227
//-----------------------------------------------------------------------------
228
void AirMapRulesetsManager::setROI(const QGCGeoBoundingCube& roi, bool reset)
229
{
230
    Q_UNUSED(reset);
231 232 233 234 235
    if (!_shared.client()) {
        qCDebug(AirMapManagerLog) << "No AirMap client instance. Not updating Airspace";
        return;
    }
    if (_state != State::Idle) {
236
        qCWarning(AirMapManagerLog) << "AirMapRulesetsManager::updateROI: state not idle";
237 238
        return;
    }
239
    qCDebug(AirMapManagerLog) << "Rulesets Request (ROI Changed)";
240
    _valid = false;
241 242 243 244 245 246
    //-- Save current selection state
    QMap<QString, bool> selectionSet;
    for(int rs = 0; rs < ruleSets()->count(); rs++) {
        AirMapRuleSet* ruleSet = qobject_cast<AirMapRuleSet*>(ruleSets()->get(rs));
        selectionSet[ruleSet->id()] = ruleSet->selected();
    }
247
    _ruleSets.clearAndDeleteContents();
248 249
    _state = State::RetrieveItems;
    RuleSets::Search::Parameters params;
250 251
    //-- Geometry: Polygon
    Geometry::Polygon polygon;
252 253
    //-- Get ROI bounding box, clipping to max area of interest
    for (const auto& qcoord : roi.polygon2D(qgcApp()->toolbox()->airspaceManager()->maxAreaOfInterest())) {
254 255 256 257 258 259
        Geometry::Coordinate coord;
        coord.latitude  = qcoord.latitude();
        coord.longitude = qcoord.longitude();
        polygon.outer_ring.coordinates.push_back(coord);
    }
    params.geometry = Geometry(polygon);
260 261
    std::weak_ptr<LifetimeChecker> isAlive(_instance);
    _shared.client()->rulesets().search(params,
262
            [this, isAlive, selectionSet](const RuleSets::Search::Result& result) {
263 264 265
        if (!isAlive.lock()) return;
        if (_state != State::RetrieveItems) return;
        if (result) {
266 267 268 269 270 271 272 273 274 275
            const std::vector<RuleSet> rulesets = result.value();
            qCDebug(AirMapManagerLog) << "Successful rulesets search. Items:" << rulesets.size();
            for (const auto& ruleset : rulesets) {
                AirMapRuleSet* pRuleSet = new AirMapRuleSet(this);
                connect(pRuleSet, &AirspaceRuleSet::selectedChanged, this, &AirMapRulesetsManager::_selectedChanged);
                pRuleSet->_id          = QString::fromStdString(ruleset.id);
                pRuleSet->_name        = QString::fromStdString(ruleset.name);
                pRuleSet->_shortName   = QString::fromStdString(ruleset.short_name);
                pRuleSet->_description = QString::fromStdString(ruleset.description);
                pRuleSet->_isDefault   = ruleset.is_default;
276 277 278 279 280 281 282
                //-- Restore selection set (if any)
                if(selectionSet.contains(pRuleSet->id())) {
                    pRuleSet->_selected = selectionSet[pRuleSet->id()];
                } else {
                    if(pRuleSet->_isDefault) {
                        pRuleSet->_selected = true;
                    }
283 284 285
                }
                switch(ruleset.selection_type) {
                case RuleSet::SelectionType::pickone:
286
                    pRuleSet->_selectionType = AirspaceRuleSet::Pickone;
287 288
                    break;
                case RuleSet::SelectionType::required:
289 290
                    pRuleSet->_selectionType = AirspaceRuleSet::Required;
                    pRuleSet->_selected = true;
291 292
                    break;
                case RuleSet::SelectionType::optional:
293
                    pRuleSet->_selectionType = AirspaceRuleSet::Optional;
294 295
                    break;
                }
296 297 298
                //-- Iterate Rules
                for (const auto& rule : ruleset.rules) {
                    AirMapRule* pRule = new AirMapRule(rule, this);
Gus Grubba's avatar
Gus Grubba committed
299
                    //-- Iterate Rule Features
Gus Grubba's avatar
Gus Grubba committed
300 301 302 303
                    for (const auto& feature : rule.features) {
                        AirMapRuleFeature* pFeature = new AirMapRuleFeature(feature, this);
                        pRule->_features.append(pFeature);
                    }
304 305 306 307
                    pRuleSet->_rules.append(pRule);
                }
                //-- Sort rules by display order
                std::sort(pRuleSet->_rules.objectList()->begin(), pRuleSet->_rules.objectList()->end(), rules_sort);
308
                _ruleSets.append(pRuleSet);
Gus Grubba's avatar
Gus Grubba committed
309
                qCDebug(AirMapManagerLog) << "Adding ruleset" << pRuleSet->name();
310
                /*
311 312 313 314 315 316 317 318
                qDebug() << "------------------------------------------";
                qDebug() << "Jurisdiction:" << ruleset.jurisdiction.name.data() << (int)ruleset.jurisdiction.region;
                qDebug() << "Name:        " << ruleset.name.data();
                qDebug() << "Short Name:  " << ruleset.short_name.data();
                qDebug() << "Description: " << ruleset.description.data();
                qDebug() << "Is default:  " << ruleset.is_default;
                qDebug() << "Applicable to these airspace types:";
                for (const auto& airspaceType : ruleset.airspace_types) {
Gus Grubba's avatar
Gus Grubba committed
319
                    qDebug() << "  " << airspaceType.data();
320 321
                }
                qDebug() << "Rules:";
322
                for (const auto& rule : ruleset.rules) {
323
                    qDebug() << "    --------------------------------------";
Gus Grubba's avatar
Gus Grubba committed
324 325 326 327 328 329
                    qDebug() << "    short_text:   " << rule.short_text.data();
                    qDebug() << "    description:  " << rule.description.data();
                    qDebug() << "    display_order:" << rule.display_order;
                    qDebug() << "    status:       " << (int)rule.status;
                    qDebug() << "            ------------------------------";
                    qDebug() << "            Features:";
330
                    for (const auto& feature : rule.features) {
Gus Grubba's avatar
Gus Grubba committed
331 332 333 334 335 336
                        qDebug() << "            name:       " << feature.name.data();
                        qDebug() << "            description:" << feature.description.data();
                        qDebug() << "            status:     " << (int)feature.status;
                        qDebug() << "            type:       " << (int)feature.type;
                        qDebug() << "            measurement:" << (int)feature.measurement;
                        qDebug() << "            unit:       " << (int)feature.unit;
337 338
                    }
                }
Gus Grubba's avatar
Gus Grubba committed
339
                */
340
            }
341
            _valid = true;
342 343
        } else {
            QString description = QString::fromStdString(result.error().description() ? result.error().description().get() : "");
344
            emit error("Failed to retrieve RuleSets", QString::fromStdString(result.error().message()), description);
345 346
        }
        _state = State::Idle;
347 348
        emit ruleSetsChanged();
        emit selectedRuleSetsChanged();
349 350
    });
}
351 352 353

//-----------------------------------------------------------------------------
QString
354
AirMapRulesetsManager::selectedRuleSets()
355
{
356
    QString selection;
357 358
    for(int i = 0; i < _ruleSets.count(); i++) {
        AirMapRuleSet* rule = qobject_cast<AirMapRuleSet*>(_ruleSets.get(i));
359 360
        if(rule && rule->selected()) {
            selection += rule->shortName() + ", ";
361 362
        }
    }
363 364 365 366 367
    int idx = selection.lastIndexOf(", ");
    if(idx >= 0) {
        selection = selection.left(idx);
    }
    return selection;
368 369 370 371
}

//-----------------------------------------------------------------------------
void
372
AirMapRulesetsManager::_selectedChanged()
373
{
374
    emit selectedRuleSetsChanged();
375 376
    //-- TODO: Do whatever it is you do to select a rule
}
377 378 379 380 381 382 383 384 385 386 387

//-----------------------------------------------------------------------------
void
AirMapRulesetsManager::clearAllFeatures()
{
    QSettings settings;
    settings.beginGroup(kAirMapFeatureGroup);
    settings.remove("");
    settings.endGroup();
}