WimaController.cc 15 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1
#include "WimaController.h"
2

3 4 5
const char* WimaController::wimaFileExtension   = "wima";
const char* WimaController::areaItemsName       = "AreaItems";
const char* WimaController::missionItemsName    = "MissionItems";
6

7 8 9 10
WimaController::WimaController(QObject *parent)
    : QObject               (parent)
    , _container            (nullptr)
    , _joinedArea           (this)
11 12
    , _measurementArea      (this)
    , _serviceArea          (this)
13
    , _corridor             (this)
14
    , _localPlanDataValid   (false)
15 16
    , _startWaypointIndex   (1)
    , _endWaypointIndex     (1)
17
{
18

19 20
}

21
QmlObjectListModel* WimaController::visualItems()
22
{
23
    return &_visualItems;
24 25
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
26 27 28 29
QStringList WimaController::loadNameFilters() const
{
    QStringList filters;

30
    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
Valentin Platzgummer's avatar
Valentin Platzgummer committed
31 32
               tr("All Files (*.*)");
    return filters;
33 34 35 36 37
}

QStringList WimaController::saveNameFilters() const
{
    QStringList filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
38

39 40
    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
    return filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
41 42
}

43
WimaDataContainer *WimaController::dataContainer()
44
{
45
    return _container;
46 47
}

48 49 50 51 52
QmlObjectListModel *WimaController::missionItems()
{
    return &_missionItems;
}

53 54 55 56 57
QmlObjectListModel *WimaController::currentMissionItems()
{
    return &_currentMissionItems;
}

58 59 60 61 62
QVariantList WimaController::waypointPath()
{
    return  _waypointPath;
}

63 64 65 66 67
QVariantList WimaController::currentWaypointPath()
{
    return _currentWaypointPath;
}

68 69 70 71 72 73 74 75 76 77 78 79
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
    emit masterControllerChanged();
}

void WimaController::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
    emit missionControllerChanged();
}

80 81 82 83 84 85
/*!
 * \fn void WimaController::setDataContainer(WimaDataContainer *container)
 * Sets the pointer to the \c WimaDataContainer, which is meant to exchange data between the \c WimaController and the \c WimaPlaner.
 *
 * \sa WimaPlaner, WimaDataContainer, WimaPlanData
 */
86 87
void WimaController::setDataContainer(WimaDataContainer *container)
{
88 89 90 91 92
    if (container != nullptr) {
        if (_container != nullptr) {
           disconnect(_container, &WimaDataContainer::dataValidChanged, this, &WimaController::containerDataValidChanged);
        }

93
        _container = container;
94
        connect(_container, &WimaDataContainer::dataValidChanged, this, &WimaController::containerDataValidChanged);
95 96 97 98 99

        emit dataContainerChanged();
    }
}

100 101 102 103 104 105
void WimaController::nextPhase()
{
    updateCurrentMissionItems();
    updateCurrentPath();
}

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
void WimaController::startMission()
{

}

void WimaController::abortMission()
{

}

void WimaController::pauseMission()
{

}

void WimaController::resumeMission()
{

}

126 127
bool WimaController::updateMission()
{
128
    return true;
129 130
}

131
void WimaController::saveToCurrent()
132 133 134
{
}

135 136
void WimaController::saveToFile(const QString& filename)
{
137
    QString file = filename;
138 139
}

140
bool WimaController::loadFromCurrent()
141
{
142
    return true;
143 144 145 146
}

bool WimaController::loadFromFile(const QString &filename)
{
147
    QString file = filename;
148
    return true;
149 150 151
}


152

153
QJsonDocument WimaController::saveToJson(FileType fileType)
154
{
155 156 157 158
    if(fileType)
    {

    }
159
    return QJsonDocument();
160 161
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
bool WimaController::calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QList<QGeoCoordinate> &path)
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
    QList<QPointF> path2D;
    bool retVal = PolygonCalculus::shortestPath(
                                   toQPolygonF(toCartesian2D(_joinedArea.coordinateList(), /*origin*/ start)),
                                   /*start point*/ QPointF(0,0),
                                   /*destination*/ toCartesian2D(destination, start),
                                   /*shortest path*/ path2D);
    path.append(toGeo(path2D, /*origin*/ start));

    return  retVal;
}

177
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QList<QGeoCoordinate> &coordinateList)
178 179 180 181
{
    return extractCoordinateList(missionItems, coordinateList, 0, missionItems.count()-1);
}

182
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QList<QGeoCoordinate> &coordinateList, int startIndex, int endIndex)
183 184 185 186 187 188 189 190 191 192 193 194
{
    if (   startIndex >= 0
        && startIndex < missionItems.count()
        && endIndex >= 0
        && endIndex < missionItems.count()) {
        if (startIndex > endIndex) {
            if (!extractCoordinateList(missionItems, coordinateList, startIndex, missionItems.count()-1))
                return false;
            if (!extractCoordinateList(missionItems, coordinateList, 0, endIndex))
                return false;
        } else {
            for (int i = startIndex; i <= endIndex; i++) {
195
                SimpleMissionItem *mItem = missionItems.value<SimpleMissionItem *>(i);
196 197 198 199 200 201 202 203 204 205 206 207 208 209

                if (mItem == nullptr) {
                    coordinateList.clear();
                    return false;
                }
                coordinateList.append(mItem->coordinate());
            }
        }
    } else
        return false;

    return true;
}

210
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList)
211 212 213 214
{
    return extractCoordinateList(missionItems, coordinateList, 0 , missionItems.count()-1);
}

215
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList, int startIndex, int endIndex)
216 217 218 219 220 221 222 223 224 225 226 227 228 229
{
    QList<QGeoCoordinate> geoCoordintateList;

    bool retValue = extractCoordinateList(missionItems, geoCoordintateList, startIndex, endIndex);

    if (!retValue)
        return false;

    for (auto coordinate : geoCoordintateList)
        coordinateList.append(QVariant::fromValue(coordinate));

    return true;
}

230 231 232 233 234 235 236 237
/*!
 * \fn void WimaController::containerDataValidChanged(bool valid)
 * Pulls plan data generated by \c WimaPlaner from the \c _container if the data is valid (\a valid equals true).
 * Is connected to the dataValidChanged() signal of the \c WimaDataContainer.
 *
 * \sa WimaDataContainer, WimaPlaner, WimaPlanData
 */
void WimaController::containerDataValidChanged(bool valid)
238
{
239 240 241 242 243
    if ( valid ) {
        if (_container == nullptr) {
            qWarning("WimaController::containerDataValidChanged(): No container assigned!");
        }
        _localPlanDataValid = false;
244
        _visualItems.clear();
245
        _missionItems.clear();
246 247 248 249 250 251 252 253 254 255 256
        WimaPlanData planData = _container->pull();

        // extract list with WimaAreas
        QList<const WimaAreaData*> areaList = planData.areaList();

        int areaCounter = 0;
        int numAreas = 4; // extract only numAreas Areas, if there are more they are invalid and ignored
        for (int i = 0; i < areaList.size(); i++) {
            const WimaAreaData *areaData = areaList[i];

            if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
257
                _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
258
                areaCounter++;
259
                _visualItems.append(&_serviceArea);
260 261 262 263

                continue;
            }

264 265
            if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
                _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
266
                areaCounter++;
267
                _visualItems.append(&_measurementArea);
268 269 270 271

                continue;
            }

272 273
            if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
                _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
274
                areaCounter++;
275
                //_visualItems.append(&_corridor); // not needed
276 277 278 279

                continue;
            }

280 281
            if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
                _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
282
                areaCounter++;
283
                _visualItems.append(&_joinedArea);
284 285

                continue;
286
            }
287 288 289 290 291

            if (areaCounter >= numAreas)
                break;
        }

292 293 294 295 296
#ifdef QT_DEBUG
        //qWarning("containerDataValidChanged(): count:");
        //qWarning() << planData.missionItems().count();
#endif

297 298
        QList<const MissionItem*> tempMissionItems = planData.missionItems();

299
        // create mission items
300
        _missionController->removeAll();
301
        QmlObjectListModel* missionControllerVisualItems = _missionController->visualItems();
302 303
        bool copyON = false;
        for ( int i = 0; i < tempMissionItems.size(); i++) {
304
            const MissionItem *missionItem = tempMissionItems[i];
305 306 307 308 309 310 311 312 313
            if (copyON || missionItem->command() == MAV_CMD_NAV_VTOL_TAKEOFF
                       || missionItem->command() == MAV_CMD_NAV_TAKEOFF) {
                _missionController->insertSimpleMissionItem(*missionItem, missionControllerVisualItems->count());
                copyON = true;
            }

            if (    missionItem->command() == MAV_CMD_NAV_VTOL_LAND
                 || missionItem->command() == MAV_CMD_NAV_LAND)
                break;
314
        }
315

316 317 318 319 320 321 322 323

        // copy mission items to _missionItems
//        MissionSettingsItem *settingsItem     = qobject_cast<MissionSettingsItem *>((*missionControllerVisualItems)[0]);
//        if (settingsItem == nullptr) {
//            qWarning("WimaController::containerDataValidChanged(): Nullptr at MissionSettingsItem!");
//            return;
//        }
//        _missionItems.append(settingsItem);
324 325 326 327 328 329 330 331 332 333 334

        for ( int i = 1; i < missionControllerVisualItems->count(); i++) {
            SimpleMissionItem *visualItem     = qobject_cast<SimpleMissionItem *>((*missionControllerVisualItems)[i]);
            if (visualItem == nullptr) {
                qWarning("WimaController::containerDataValidChanged(): Nullptr at SimpleMissionItem!");
                return;
            }
            SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
            _missionItems.append(visualItemCopy);
        }

335
        updateWaypointPath();
336 337

        _startWaypointIndex = 1;
338 339
        updateCurrentMissionItems();
        updateCurrentPath();
340

341 342
        if (areaCounter == numAreas)
            _localPlanDataValid = true;
343

344 345
    } else {
        _localPlanDataValid = false;
346 347
        _visualItems.clear();
        _missionItems.clear();
348
    }
349 350

    emit visualItemsChanged();
351
    emit missionItemsChanged();
352 353 354 355 356

#ifdef QT_DEBUG
    //qWarning("Mission Items count: ");
    //qWarning() << _missionItems.count();
#endif
357 358
}

359
void WimaController::updateCurrentMissionItems()
360
{
361
    int numberWaypoints = 30; // the number of waypoints currentMissionItems must not exceed
362
    int overlapping = 2; // number of overlapping waypoints of consecutive mission phases
363

364 365 366
    SimpleMissionItem *homeItem = _missionItems.value<SimpleMissionItem *>(0);
    if (homeItem == nullptr) {
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
367
        _currentMissionItems.clear();
368 369 370 371
        return;
    }
    QGeoCoordinate homeCoordinate(homeItem->coordinate());

372
    QList<QGeoCoordinate> geoCoordinateList; // list with potential waypoints (from _missionItems), for _currentMissionItems
373 374
    _endWaypointIndex = std::min(_startWaypointIndex + numberWaypoints - 1, _missionItems.count()-2); // -2 -> last item is land item
    if (!extractCoordinateList(_missionItems, geoCoordinateList, _startWaypointIndex, _endWaypointIndex)) {
375
        qWarning("WimaController::updateCurrentMissionItems(): error on waypoint extraction.");
376
        _currentMissionItems.clear();
377
        return;
378
    }
379
    _startWaypointIndex = _endWaypointIndex + 1 - overlapping;
380 381 382 383 384

    // calculate path from home to first waypoint
    QList<QGeoCoordinate> path;
    if ( !calcShortestPath(homeCoordinate, geoCoordinateList[0], path) ) {
        qWarning("WimaController::updateCurrentMissionItems(): Not able to calc path from home to first waypoint.");
385
        _currentMissionItems.clear();
386 387 388 389 390 391 392 393 394 395
        return;
    }
    // prepend to geoCoordinateList
    for (int i = path.size()-2; i >= 0; i--) //  i = path.size()-2 : last coordinate already in geoCoordinateList
        geoCoordinateList.prepend(path[i]);

    // calculate path from last waypoint to home
    path.clear();
    if ( !calcShortestPath(geoCoordinateList.last(), homeCoordinate, path) ) {
        qWarning("WimaController::updateCurrentMissionItems(): Not able to calc path from home to first waypoint.");
396
        _currentMissionItems.clear();
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
        return;
    }
    path.removeFirst(); // first coordinate already in geoCoordinateList
    geoCoordinateList.append(path);

    // create Mission Items
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisuals = _missionController->visualItems();
    for (auto coordinate : geoCoordinateList)
        _missionController->insertSimpleMissionItem(coordinate, missionControllerVisuals->count());

    // set land command for last mission item
    SimpleMissionItem *landItem = missionControllerVisuals->value<SimpleMissionItem*>(missionControllerVisuals->count()-1);
    if (landItem == nullptr) {
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
412
        _currentMissionItems.clear();
413 414 415 416 417 418 419
        return;
    }
    // check vehicle type, before setting land command
    Vehicle* controllerVehicle = _masterController->controllerVehicle();
    MAV_CMD landCmd = controllerVehicle->vtol() ? MAV_CMD_NAV_VTOL_LAND : MAV_CMD_NAV_LAND;
    if (controllerVehicle->firmwarePlugin()->supportedMissionCommands().contains(landCmd)) {
        landItem->setCommand(landCmd);
420 421
    } else {
        _currentMissionItems.clear();
422
        return;
423
    }
424 425 426 427 428 429 430 431 432

    // copy mission items to _currentMissionItems
//    MissionSettingsItem *settingsItem     = qobject_cast<MissionSettingsItem *>((*missionControllerVisuals)[0]);
//    if (settingsItem == nullptr) {
//        qWarning("WimaController::containerDataValidChanged(): Nullptr at MissionSettingsItem!");
//        return;
//    }
//    _missionItems.append(settingsItem);

433
    _currentMissionItems.clear();
434 435 436
    for ( int i = 1; i < missionControllerVisuals->count(); i++) {
        SimpleMissionItem *visualItem     = missionControllerVisuals->value<SimpleMissionItem*>(i);
        if (visualItem == nullptr) {
437 438
            qWarning("WimaController::updateCurrentMissionItems(): Nullptr at SimpleMissionItem!");            
            _currentMissionItems.clear();
439 440 441 442 443
            return;
        }
        SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
        _currentMissionItems.append(visualItemCopy);
    }
444

445
    emit currentMissionItemsChanged();
446
}
447

448 449 450
void WimaController::updateWaypointPath()
{
    _waypointPath.clear();
451 452
    if (!extractCoordinateList(_missionItems, _waypointPath, 0, _missionItems.count()-1))
        return;
453

454 455 456 457 458
    emit waypointPathChanged();
}
void WimaController::updateCurrentPath()
{
    _currentWaypointPath.clear();
459 460
    if (!extractCoordinateList(_currentMissionItems, _currentWaypointPath, 0, _currentMissionItems.count()-1))
        return;
461

462 463
    emit currentWaypointPathChanged();
}
464

465 466