Reposition: tolerate more scenarios in ATC_mgr
When starting at an airport, but not at parking or a runway, create an empty AIFlightPlan, and ensure the AIManager code doesn’t choke on empty FPs. Add a unit-test which simulates the C172 tutorial reposition logic, which is a little gnarly.
This commit is contained in:
parent
46435edf19
commit
423c754009
7 changed files with 113 additions and 31 deletions
|
@ -1022,7 +1022,7 @@ void FGAIAircraft::controlSpeed(FGAIWaypoint* curr, FGAIWaypoint* next) {
|
|||
* Update target values (heading, alt, speed) depending on flight plan or control properties
|
||||
*/
|
||||
void FGAIAircraft::updatePrimaryTargetValues(double dt, bool& flightplanActive, bool& aiOutOfSight) {
|
||||
if (fp) // AI object has a flightplan
|
||||
if (fp && fp->isValidPlan()) // AI object has a flightplan
|
||||
{
|
||||
//TODO make this a function of AIBase
|
||||
time_t now = globals->get_time_params()->get_cur_time();
|
||||
|
|
|
@ -327,12 +327,15 @@ FGAIWaypoint* FGAIFlightPlan::getLastWaypoint()
|
|||
|
||||
FGAIWaypoint* FGAIFlightPlan::getPreviousWaypoint( void ) const
|
||||
{
|
||||
if (wpt_iterator == waypoints.begin()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
wpt_vector_iterator prev = wpt_iterator;
|
||||
return *(--prev);
|
||||
}
|
||||
if (empty())
|
||||
return nullptr;
|
||||
|
||||
if (wpt_iterator == waypoints.begin()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
wpt_vector_iterator prev = wpt_iterator;
|
||||
return *(--prev);
|
||||
}
|
||||
}
|
||||
|
||||
FGAIWaypoint* FGAIFlightPlan::getCurrentWaypoint( void ) const
|
||||
|
@ -357,6 +360,9 @@ FGAIWaypoint* FGAIFlightPlan::getNextWaypoint( void ) const
|
|||
|
||||
void FGAIFlightPlan::IncrementWaypoint(bool eraseWaypoints )
|
||||
{
|
||||
if (empty())
|
||||
return;
|
||||
|
||||
if (eraseWaypoints)
|
||||
{
|
||||
if (wpt_iterator == waypoints.begin())
|
||||
|
@ -376,11 +382,17 @@ void FGAIFlightPlan::IncrementWaypoint(bool eraseWaypoints )
|
|||
|
||||
void FGAIFlightPlan::DecrementWaypoint()
|
||||
{
|
||||
wpt_iterator--;
|
||||
if (empty())
|
||||
return;
|
||||
|
||||
wpt_iterator--;
|
||||
}
|
||||
|
||||
void FGAIFlightPlan::eraseLastWaypoint()
|
||||
{
|
||||
if (empty())
|
||||
return;
|
||||
|
||||
delete (waypoints.back());
|
||||
waypoints.pop_back();;
|
||||
wpt_iterator = waypoints.begin();
|
||||
|
@ -559,3 +571,15 @@ FGAirportRef FGAIFlightPlan::arrivalAirport() const
|
|||
{
|
||||
return arrival;
|
||||
}
|
||||
|
||||
FGAIFlightPlan* FGAIFlightPlan::createDummyUserPlan()
|
||||
{
|
||||
FGAIFlightPlan* fp = new FGAIFlightPlan;
|
||||
fp->isValid = false;
|
||||
return fp;
|
||||
}
|
||||
|
||||
bool FGAIFlightPlan::empty() const
|
||||
{
|
||||
return waypoints.empty();
|
||||
}
|
||||
|
|
|
@ -150,6 +150,12 @@ public:
|
|||
~FGAIFlightPlan();
|
||||
|
||||
/**
|
||||
@brief create a neatrly empty FlightPlan for the user aircraft, based
|
||||
on the current position and route-manager data.
|
||||
*/
|
||||
static FGAIFlightPlan* createDummyUserPlan();
|
||||
/**
|
||||
|
||||
@brief read a flight-plan from a file.
|
||||
All current contents of the flight-plan are replaxced, and the current waypoint is reset to the beginning
|
||||
*/
|
||||
|
@ -220,6 +226,9 @@ public:
|
|||
|
||||
FGAirportRef departureAirport() const;
|
||||
FGAirportRef arrivalAirport() const;
|
||||
|
||||
bool empty() const;
|
||||
|
||||
private:
|
||||
FGAIFlightPlan *sid;
|
||||
typedef std::vector <FGAIWaypoint*> wpt_vector_type;
|
||||
|
|
|
@ -191,19 +191,20 @@ void FGATCManager::postinit()
|
|||
userAircraft->setTakeOffStatus(2);
|
||||
} else {
|
||||
// We're on the ground somewhere. Handle this case later.
|
||||
|
||||
|
||||
// important : we are on the ground, so reset the AIFlightPlan back to
|
||||
// an empty one. Otherwise, in the reposition case, we end up with a
|
||||
// a dummy one. Otherwise, in the reposition case, we end up with a
|
||||
// stale flight-plan which confuses other code (eg, PositionInit::finalizeForParking)
|
||||
// see unit test: PosInitTests::testRepositionAtOccupied
|
||||
fp.reset(FGAIFlightPlan::createDummyUserPlan());
|
||||
userAircraft->FGAIBase::setFlightPlan(std::move(fp));
|
||||
controller = nullptr;
|
||||
|
||||
initSucceeded = true; // should be false?
|
||||
return;
|
||||
}
|
||||
|
||||
if (fp) {
|
||||
|
||||
if (fp && !fp->empty()) {
|
||||
fp->getLastWaypoint()->setName( fp->getLastWaypoint()->getName() + string("legend"));
|
||||
}
|
||||
} else {
|
||||
|
@ -222,7 +223,9 @@ void FGATCManager::postinit()
|
|||
|
||||
if (controller) {
|
||||
FGAIFlightPlan* plan = userAircraft->GetFlightPlan();
|
||||
controller->announcePosition(userAircraft->getID(), plan, plan->getCurrentWaypoint()->getRouteIndex(),
|
||||
const int routeIndex = (plan && plan->getCurrentWaypoint()) ? plan->getCurrentWaypoint()->getRouteIndex() : 0;
|
||||
controller->announcePosition(userAircraft->getID(), plan,
|
||||
routeIndex,
|
||||
userAircraft->_getLatitude(),
|
||||
userAircraft->_getLongitude(),
|
||||
userAircraft->_getHeading(),
|
||||
|
@ -319,7 +322,6 @@ void FGATCManager::update ( double time ) {
|
|||
// user should be able to select a new route, but for now just shut down the
|
||||
// system.
|
||||
if (size < 3) {
|
||||
SG_LOG(SG_ATC, SG_INFO, "Shutting down the atc_mgr - ran out of waypoints");
|
||||
return;
|
||||
}
|
||||
#if 0
|
||||
|
|
|
@ -600,6 +600,10 @@ bool initPosition()
|
|||
|
||||
bool set_pos = false;
|
||||
|
||||
// clear this value, so we don't preserve an old value and confuse
|
||||
// the ATC manager. We will set it again if it's valid
|
||||
fgSetString("/sim/atc/runway", "");
|
||||
|
||||
// If glideslope is specified, then calculate offset-distance or
|
||||
// altitude relative to glide slope if either of those was not
|
||||
// specified.
|
||||
|
@ -683,9 +687,6 @@ bool initPosition()
|
|||
// until position finalisation
|
||||
// the rest of the work happens in finalizePosFromParkpos
|
||||
if ( airportParkingSetVicinity( apt ) ) {
|
||||
// set tower position
|
||||
fgSetString("/sim/airport/closest-airport-id", apt.c_str());
|
||||
fgSetString("/sim/tower/airport-id", apt.c_str());
|
||||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
@ -693,10 +694,6 @@ bool initPosition()
|
|||
if ( !set_pos && !apt.empty() && !rwy_no.empty() ) {
|
||||
// An airport + runway is requested
|
||||
if ( fgSetPosFromAirportIDandRwy( apt, rwy_no, rwy_req ) ) {
|
||||
// set tower position (a little off the heading for single
|
||||
// runway airports)
|
||||
fgSetString("/sim/airport/closest-airport-id", apt.c_str());
|
||||
fgSetString("/sim/tower/airport-id", apt.c_str());
|
||||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
@ -704,14 +701,18 @@ bool initPosition()
|
|||
if ( !set_pos && !apt.empty() ) {
|
||||
// An airport is requested (find runway closest to hdg)
|
||||
if ( setPosFromAirportIDandHdg( apt, hdg ) ) {
|
||||
// set tower position (a little off the heading for single
|
||||
// runway airports)
|
||||
fgSetString("/sim/airport/closest-airport-id", apt.c_str());
|
||||
fgSetString("/sim/tower/airport-id", apt.c_str());
|
||||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if an airport ID was requested, set closest-airport-id
|
||||
// and tower based upon it.
|
||||
if (!apt.empty() && set_pos) {
|
||||
// set tower position
|
||||
fgSetString("/sim/airport/closest-airport-id", apt.c_str());
|
||||
fgSetString("/sim/tower/airport-id", apt.c_str());
|
||||
}
|
||||
|
||||
if (original_hdg < 9990.0) {
|
||||
// The user-set heading may be overridden by the setPosFromAirportID above.
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Setting heading preset to : " << original_hdg);
|
||||
|
|
|
@ -177,10 +177,6 @@ void PosInitTests::testAirportAltitudeOffsetStartup()
|
|||
|
||||
CPPUNIT_ASSERT(fgGetBool("/sim/presets/airport-requested"));
|
||||
initPosition();
|
||||
|
||||
|
||||
|
||||
initPosition();
|
||||
|
||||
SGGeod expectedPos = SGGeodesy::direct(FGAirport::getByIdent("EDDF")->geod(), 270, 5 * SG_NM_TO_METER);
|
||||
checkPosition(expectedPos, 5000.0);
|
||||
|
@ -616,6 +612,53 @@ void PosInitTests::testAirportRepositionAirport()
|
|||
|
||||
}
|
||||
|
||||
// this simulates what the C172 preflight tutorial does,
|
||||
// where the user is likely at a random airport (on a runway), and starts
|
||||
// the preflight tutorial, which repositions them to a ramp location
|
||||
// at PHTO
|
||||
void PosInitTests::testAirportRunwayRepositionAirport()
|
||||
{
|
||||
{
|
||||
Options* opts = Options::sharedInstance();
|
||||
opts->setShouldLoadDefaultConfig(false);
|
||||
const char* args[] = {"dummypath", "--airport=EDDS", "--runway=07"};
|
||||
opts->init(3, (char**)args, SGPath());
|
||||
opts->processOptions();
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(fgGetBool("/sim/presets/airport-requested"));
|
||||
CPPUNIT_ASSERT(fgGetBool("/sim/presets/runway-requested"));
|
||||
checkRunway(std::string("07"));
|
||||
initPosition();
|
||||
simulateFinalizePosition();
|
||||
|
||||
checkClosestAirport(std::string("EDDS"));
|
||||
checkPosition(FGAirport::getByIdent("EDDS")->geod(), 10000.0);
|
||||
|
||||
// Now re-position to PHTO runway
|
||||
// Reset the Lat/Lon as these will be used in preference to the airport ID
|
||||
fgSetDouble("/sim/presets/longitude-deg", -155.0597483);
|
||||
fgSetDouble("/sim/presets/latitude-deg", 19.71731272);
|
||||
fgSetString("/sim/presets/airport-id", "PHTO");
|
||||
fgSetDouble("/sim/presets/heading-deg", 125);
|
||||
fgSetBool("/sim/presets/on-ground", true);
|
||||
fgSetBool("/sim/presets/runway-requested", true);
|
||||
fgSetBool("/sim/presets/airport-requested", true);
|
||||
fgSetBool("/sim/presets/parking-requested", true);
|
||||
|
||||
simulateStartReposition();
|
||||
finalizePosition();
|
||||
|
||||
FGTestApi::runForTime(1.0);
|
||||
|
||||
checkClosestAirport(std::string("PHTO"));
|
||||
checkPosition(FGAirport::getByIdent("PHTO")->geod(), 5000.0);
|
||||
checkHeading(125);
|
||||
checkOnGround();
|
||||
|
||||
FGTestApi::runForTime(1.0);
|
||||
}
|
||||
|
||||
void PosInitTests::testParkInvalid()
|
||||
{
|
||||
{
|
||||
|
|
|
@ -46,8 +46,9 @@ class PosInitTests : public CppUnit::TestFixture
|
|||
CPPUNIT_TEST(testRepositionAtParking);
|
||||
CPPUNIT_TEST(testParkAtOccupied);
|
||||
CPPUNIT_TEST(testParkInvalid);
|
||||
|
||||
|
||||
CPPUNIT_TEST(testAirportRunwayRepositionAirport);
|
||||
|
||||
|
||||
// Navaid tests
|
||||
CPPUNIT_TEST(testVOROnlyStartup);
|
||||
CPPUNIT_TEST(testVOROffsetAltitudeHeadingStartup);
|
||||
|
@ -116,6 +117,8 @@ public:
|
|||
void testRepositionAtSameParking();
|
||||
void testRepositionAtOccupied();
|
||||
void testRepositionAtInvalid();
|
||||
void testAirportRunwayRepositionAirport();
|
||||
|
||||
private:
|
||||
// Helper functions for tests. Return void as they use CPPUNIT_ASSERT
|
||||
void checkAlt(float value);
|
||||
|
|
Loading…
Add table
Reference in a new issue