/**************************************************************************** * * (c) 2009-2020 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ #include "MissionControllerTest.h" #include "LinkManager.h" #include "MultiVehicleManager.h" #include "SimpleMissionItem.h" #include "MissionSettingsItem.h" #include "QGCApplication.h" #include "SettingsManager.h" #include "AppSettings.h" MissionControllerTest::MissionControllerTest(void) : _multiSpyMissionController(nullptr) , _multiSpyMissionItem(nullptr) , _missionController(nullptr) { } void MissionControllerTest::cleanup(void) { delete _masterController; _masterController = nullptr; delete _multiSpyMissionController; _multiSpyMissionController = nullptr; delete _multiSpyMissionItem; _multiSpyMissionItem = nullptr; MissionControllerManagerTest::cleanup(); } void MissionControllerTest::_initForFirmwareType(MAV_AUTOPILOT firmwareType) { MissionControllerManagerTest::_initForFirmwareType(firmwareType); // VisualMissionItem signals _rgVisualItemSignals[coordinateChangedSignalIndex] = SIGNAL(coordinateChanged(const QGeoCoordinate&)); // MissionController signals _rgMissionControllerSignals[visualItemsChangedSignalIndex] = SIGNAL(visualItemsChanged()); // Master controller pulls offline vehicle info from settings qgcApp()->toolbox()->settingsManager()->appSettings()->offlineEditingFirmwareClass()->setRawValue(QGCMAVLink::firmwareClass(firmwareType)); _masterController = new PlanMasterController(this); _masterController->setFlyView(false); _missionController = _masterController->missionController(); _multiSpyMissionController = new MultiSignalSpy(); Q_CHECK_PTR(_multiSpyMissionController); QCOMPARE(_multiSpyMissionController->init(_missionController, _rgMissionControllerSignals, _cMissionControllerSignals), true); _masterController->start(); // All signals should some through on start QCOMPARE(_multiSpyMissionController->checkOnlySignalsByMask(visualItemsChangedSignalMask), true); _multiSpyMissionController->clearAllSignals(); QmlObjectListModel* visualItems = _missionController->visualItems(); QVERIFY(visualItems); // Empty vehicle only has home position QCOMPARE(visualItems->count(), 1); // Mission Settings should be in first slot MissionSettingsItem* settingsItem = visualItems->value(0); QVERIFY(settingsItem); // Offline vehicle, so no home position QCOMPARE(settingsItem->coordinate().isValid(), false); // Empty mission, so no child items possible QCOMPARE(settingsItem->childItems()->count(), 0); // No waypoint lines QmlObjectListModel* simpleFlightPathSegments = _missionController->simpleFlightPathSegments(); QVERIFY(simpleFlightPathSegments); QCOMPARE(simpleFlightPathSegments->count(), 0); } void MissionControllerTest::_testEmptyVehicleWorker(MAV_AUTOPILOT firmwareType) { _initForFirmwareType(firmwareType); // FYI: A significant amount of empty vehicle testing is in _initForFirmwareType since that // sets up an empty vehicle QmlObjectListModel* visualItems = _missionController->visualItems(); QVERIFY(visualItems); VisualMissionItem* visualItem = visualItems->value(0); QVERIFY(visualItem); _setupVisualItemSignals(visualItem); } void MissionControllerTest::_testEmptyVehiclePX4(void) { _testEmptyVehicleWorker(MAV_AUTOPILOT_PX4); } void MissionControllerTest::_testEmptyVehicleAPM(void) { _testEmptyVehicleWorker(MAV_AUTOPILOT_ARDUPILOTMEGA); } #if 0 void MissionControllerTest::_testOfflineToOnlineWorker(MAV_AUTOPILOT firmwareType) { // Start offline and add item _missionController = new MissionController(); Q_CHECK_PTR(_missionController); _missionController->start(false /* flyView */); _missionController->insertSimpleMissionItem(QGeoCoordinate(37.803784, -122.462276), _missionController->visualItems()->count()); // Go online to empty vehicle MissionControllerManagerTest::_initForFirmwareType(firmwareType); #if 1 // Due to current limitations, offline items will go away QCOMPARE(_missionController->visualItems()->count(), 1); #else //Make sure our offline mission items are still there QCOMPARE(_missionController->visualItems()->count(), 2); #endif } void MissionControllerTest::_testOfflineToOnlineAPM(void) { _testOfflineToOnlineWorker(MAV_AUTOPILOT_ARDUPILOTMEGA); } void MissionControllerTest::_testOfflineToOnlinePX4(void) { _testOfflineToOnlineWorker(MAV_AUTOPILOT_PX4); } #endif void MissionControllerTest::_setupVisualItemSignals(VisualMissionItem* visualItem) { delete _multiSpyMissionItem; _multiSpyMissionItem = new MultiSignalSpy(); Q_CHECK_PTR(_multiSpyMissionItem); QCOMPARE(_multiSpyMissionItem->init(visualItem, _rgVisualItemSignals, _cVisualItemSignals), true); } void MissionControllerTest::_testGimbalRecalc(void) { _initForFirmwareType(MAV_AUTOPILOT_PX4); _missionController->insertSimpleMissionItem(QGeoCoordinate(0, 0), 1); _missionController->insertSimpleMissionItem(QGeoCoordinate(0, 0), 2); _missionController->insertSimpleMissionItem(QGeoCoordinate(0, 0), 3); _missionController->insertSimpleMissionItem(QGeoCoordinate(0, 0), 4); // No specific gimbal yaw set yet for (int i=1; i<_missionController->visualItems()->count(); i++) { VisualMissionItem* visualItem = _missionController->visualItems()->value(i); QVERIFY(qIsNaN(visualItem->missionGimbalYaw())); } // Specify gimbal yaw on settings item should generate yaw on all subsequent items const int yawIndex = 2; SimpleMissionItem* item = _missionController->visualItems()->value(yawIndex); item->cameraSection()->setSpecifyGimbal(true); item->cameraSection()->gimbalYaw()->setRawValue(0.0); QTest::qWait(100); // Recalcs in MissionController are queued to remove dups. Allow return to main message loop. for (int i=1; i<_missionController->visualItems()->count(); i++) { //qDebug() << i; VisualMissionItem* visualItem = _missionController->visualItems()->value(i); if (i >= yawIndex) { QCOMPARE(visualItem->missionGimbalYaw(), 0.0); } else { QVERIFY(qIsNaN(visualItem->missionGimbalYaw())); } } } void MissionControllerTest::_testVehicleYawRecalc(void) { _initForFirmwareType(MAV_AUTOPILOT_PX4); double wpDistance = 1000; double wpAngleInc = 45; double wpAngle = 0; int cMissionItems = 4; QGeoCoordinate currentCoord(0, 0); _missionController->insertSimpleMissionItem(currentCoord, 1); for (int i=2; i<=cMissionItems; i++) { wpAngle += wpAngleInc; currentCoord = currentCoord.atDistanceAndAzimuth(wpDistance, wpAngle); _missionController->insertSimpleMissionItem(currentCoord, i); } QTest::qWait(100); // Recalcs in MissionController are queued to remove dups. Allow return to main message loop. // No specific vehicle yaw set yet. Vehicle yaw should track flight path. double expectedVehicleYaw = wpAngleInc; for (int i=2; ivisualItems()->value(i); QCOMPARE(visualItem->missionVehicleYaw(), expectedVehicleYaw); if (i <= cMissionItems - 1) { expectedVehicleYaw += wpAngleInc; } } SimpleMissionItem* simpleItem = _missionController->visualItems()->value(3); simpleItem->missionItem().setParam4(66); QTest::qWait(100); // Recalcs in MissionController are queued to remove dups. Allow return to main message loop. // All item should track vehicle path except for the one changed expectedVehicleYaw = wpAngleInc; for (int i=2; ivisualItems()->value(i); QCOMPARE(visualItem->missionVehicleYaw(), i == 3 ? 66.0 : expectedVehicleYaw); if (i <= cMissionItems - 1) { expectedVehicleYaw += wpAngleInc; } } } void MissionControllerTest::_testLoadJsonSectionAvailable(void) { _initForFirmwareType(MAV_AUTOPILOT_PX4); _masterController->loadFromFile(":/unittest/SectionTest.plan"); QmlObjectListModel* visualItems = _missionController->visualItems(); QVERIFY(visualItems); QCOMPARE(visualItems->count(), 5); // Check that only waypoint items have camera and speed sections for (int i=1; icount(); i++) { SimpleMissionItem* item = visualItems->value(i); QVERIFY(item); if ((int)item->command() == MAV_CMD_NAV_WAYPOINT) { QCOMPARE(item->cameraSection()->available(), true); QCOMPARE(item->speedSection()->available(), true); } else { QCOMPARE(item->cameraSection()->available(), false); QCOMPARE(item->speedSection()->available(), false); } } } void MissionControllerTest::_testGlobalAltMode(void) { _initForFirmwareType(MAV_AUTOPILOT_PX4); struct _globalAltMode_s { QGroundControlQmlGlobal::AltitudeMode altMode; MAV_FRAME expectedMavFrame; } altModeTestCases[] = { { QGroundControlQmlGlobal::AltitudeModeRelative, MAV_FRAME_GLOBAL_RELATIVE_ALT }, { QGroundControlQmlGlobal::AltitudeModeAbsolute, MAV_FRAME_GLOBAL }, { QGroundControlQmlGlobal::AltitudeModeAboveTerrain, MAV_FRAME_GLOBAL }, { QGroundControlQmlGlobal::AltitudeModeTerrainFrame, MAV_FRAME_GLOBAL_TERRAIN_ALT }, }; for (const _globalAltMode_s& testCase: altModeTestCases) { _missionController->removeAll(); _missionController->setGlobalAltitudeMode(testCase.altMode); _missionController->insertTakeoffItem(QGeoCoordinate(0, 0), 1); _missionController->insertSimpleMissionItem(QGeoCoordinate(0, 0), 2); _missionController->insertSimpleMissionItem(QGeoCoordinate(0, 0), 3); _missionController->insertSimpleMissionItem(QGeoCoordinate(0, 0), 4); SimpleMissionItem* si = qobject_cast(_missionController->visualItems()->value(1)); QCOMPARE(si->altitudeMode(), QGroundControlQmlGlobal::AltitudeModeRelative); QCOMPARE(si->missionItem().frame(), MAV_FRAME_GLOBAL_RELATIVE_ALT); for (int i=2; i<_missionController->visualItems()->count(); i++) { qDebug() << i; SimpleMissionItem* si = qobject_cast(_missionController->visualItems()->value(i)); QCOMPARE(si->altitudeMode(), testCase.altMode); QCOMPARE(si->missionItem().frame(), testCase.expectedMavFrame); } } }