From afcdbd3158503773644aa07dd86cd92d67946bd4 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 1 Oct 2012 17:18:36 +0100 Subject: [PATCH] Overhaul the ground-net / parking code. Use the nav-data-cache to cache groundnet information, including parking positions and the taxi-node graph. --- src/AIModel/AIAircraft.cxx | 22 +- src/AIModel/AIFlightPlan.cxx | 40 +- src/AIModel/AIFlightPlan.hxx | 17 +- src/AIModel/AIFlightPlanCreate.cxx | 127 +++--- src/AIModel/AIFlightPlanCreatePushBack.cxx | 115 +++--- src/AIModel/performancedata.cxx | 5 + src/AIModel/performancedata.hxx | 1 + src/ATC/atc_mgr.cxx | 14 +- src/ATC/trafficcontrol.cxx | 6 +- src/Airports/dynamicloader.cxx | 65 ++- src/Airports/dynamicloader.hxx | 15 + src/Airports/dynamics.cxx | 222 ++++++---- src/Airports/dynamics.hxx | 56 ++- src/Airports/gnnode.cxx | 45 +- src/Airports/gnnode.hxx | 49 +-- src/Airports/groundnetwork.cxx | 451 +++++++-------------- src/Airports/groundnetwork.hxx | 122 ++---- src/Airports/parking.cxx | 11 +- src/Airports/parking.hxx | 33 +- src/Airports/simple.cxx | 3 +- src/Airports/xmlloader.cxx | 31 +- src/Main/positioninit.cxx | 16 +- src/Navaids/NavDataCache.cxx | 247 ++++++++++- src/Navaids/NavDataCache.hxx | 68 +++- src/Navaids/positioned.hxx | 2 +- src/Scripting/NasalPositioned.cxx | 12 +- 26 files changed, 1016 insertions(+), 779 deletions(-) diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index 2db78de6d..4f02828fb 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -862,7 +862,8 @@ bool FGAIAircraft::handleAirportEndPoints(FGAIWaypoint* prev, time_t now) { //cerr << trafficRef->getCallSign() << " has passed waypoint " << prev->name << " at speed " << speed << endl; //cerr << "Passing waypoint : " << prev->getName() << endl; if (prev->contains("PushBackPoint")) { - dep->getDynamics()->releaseParking(fp->getGate()); + // clearing the parking assignment will release the gate + fp->setGate(ParkingAssignment()); AccelTo(0.0); //setTaxiClearanceRequest(true); } @@ -1209,18 +1210,15 @@ void FGAIAircraft::updatePitchAngleTarget() { } } -string FGAIAircraft::atGate() { - string tmp(""); - if (fp->getLeg() < 3) { - if (trafficRef) { - if (fp->getGate() > 0) { - FGParking *park = - trafficRef->getDepartureAirport()->getDynamics()->getParking(fp->getGate()); - tmp = park->getName(); - } - } +string FGAIAircraft::atGate() +{ + if ((fp->getLeg() < 3) && trafficRef) { + if (fp->getParkingGate()) { + return fp->getParkingGate()->ident(); + } } - return tmp; + + return string(); } void FGAIAircraft::handleATCRequests() { diff --git a/src/AIModel/AIFlightPlan.cxx b/src/AIModel/AIFlightPlan.cxx index 14c4de1ac..df5cbf64f 100644 --- a/src/AIModel/AIFlightPlan.cxx +++ b/src/AIModel/AIFlightPlan.cxx @@ -105,9 +105,8 @@ FGAIFlightPlan::FGAIFlightPlan() start_time = 0; arrivalTime = 0; leg = 10; - gateId = 0; lastNodeVisited = 0; - taxiRoute = 0; + // taxiRoute = 0; wpt_iterator = waypoints.begin(); isValid = true; } @@ -121,9 +120,8 @@ FGAIFlightPlan::FGAIFlightPlan(const string& filename) start_time = 0; arrivalTime = 0; leg = 10; - gateId = 0; lastNodeVisited = 0; - taxiRoute = 0; +// taxiRoute = 0; isValid = parseProperties(filename); @@ -161,9 +159,8 @@ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac, start_time = start; arrivalTime = 0; leg = 10; - gateId = 0; lastNodeVisited = 0; - taxiRoute = 0; + // taxiRoute = 0; if (parseProperties(p)) { isValid = true; @@ -176,16 +173,7 @@ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac, FGAIFlightPlan::~FGAIFlightPlan() { deleteWaypoints(); - delete taxiRoute; - -// if we're parked at a gate, release it - if (gateId >= 0) { - FGAirport* apt = (leg >= 7) ? arrival : departure; - if (apt) { - SG_LOG(SG_AI, SG_INFO, "releasing parking gate " << gateId << " at " << apt->ident()); - apt->getDynamics()->releaseParking(gateId); - } - } + //delete taxiRoute; } void FGAIFlightPlan::createWaypoints(FGAIAircraft *ac, @@ -227,7 +215,6 @@ void FGAIFlightPlan::createWaypoints(FGAIAircraft *ac, isValid = create(ac, dep, arr, leg, alt, speed, lat, lon, firstLeg, radius, fltType, acType, airline, dist); wpt_iterator = waypoints.begin(); - } bool FGAIFlightPlan::parseProperties(const std::string& filename) @@ -454,14 +441,6 @@ void FGAIFlightPlan::restart() wpt_iterator = waypoints.begin(); } - -void FGAIFlightPlan::deleteTaxiRoute() -{ - delete taxiRoute; - taxiRoute = 0; -} - - int FGAIFlightPlan::getRouteIndex(int i) { if ((i > 0) && (i < (int)waypoints.size())) { return waypoints[i]->getRouteIndex(); @@ -470,7 +449,6 @@ int FGAIFlightPlan::getRouteIndex(int i) { return 0; } - double FGAIFlightPlan::checkTrackLength(string wptName) { // skip the first two waypoints: first one is behind, second one is partially done; double trackDistance = 0; @@ -494,3 +472,13 @@ void FGAIFlightPlan::shortenToFirst(unsigned int number, string name) } (waypoints.back())->setName((waypoints.back())->getName() + name); } + +void FGAIFlightPlan::setGate(ParkingAssignment pka) +{ + gate = pka; +} + +FGParking* FGAIFlightPlan::getParkingGate() +{ + return gate.parking(); +} diff --git a/src/AIModel/AIFlightPlan.hxx b/src/AIModel/AIFlightPlan.hxx index 5b5586af1..94476aa8d 100644 --- a/src/AIModel/AIFlightPlan.hxx +++ b/src/AIModel/AIFlightPlan.hxx @@ -25,6 +25,8 @@ #include #include #include +#include +#include // forward decls class FGTaxiRoute; @@ -139,8 +141,7 @@ public: void setLeg(int val) { leg = val;} void setTime(time_t st) { start_time = st; } - int getGate() const { return gateId; } - void setGate(int id) { gateId = id; }; + double getLeadInAngle() const { return leadInAngle; } const std::string& getRunway() const; @@ -149,9 +150,9 @@ public: bool getRepeat(void) const { return repeat; } void restart(void); int getNrOfWayPoints() { return waypoints.size(); } - int getRouteIndex(int i); // returns the AI related index of this current routes. - FGTaxiRoute *getTaxiRoute() { return taxiRoute; } - void deleteTaxiRoute(); + + int getRouteIndex(int i); // returns the AI related index of this current routes. + std::string getRunway() { return activeRunway; } bool isActive(time_t time) {return time >= this->getStartTime();} @@ -172,6 +173,8 @@ public: void shortenToFirst(unsigned int number, std::string name); + void setGate(ParkingAssignment pka); + FGParking* getParkingGate(); private: FGAIFlightPlan *sid; typedef std::vector wpt_vector_type; @@ -188,9 +191,9 @@ private: time_t start_time; time_t arrivalTime; // For AI/ATC purposes. int leg; - int gateId, lastNodeVisited; + ParkingAssignment gate; + PositionedID lastNodeVisited; std::string activeRunway; - FGTaxiRoute *taxiRoute; std::string name; bool isValid; FGAirportRef departure, arrival; diff --git a/src/AIModel/AIFlightPlanCreate.cxx b/src/AIModel/AIFlightPlanCreate.cxx index 795aceadb..92b901bce 100644 --- a/src/AIModel/AIFlightPlanCreate.cxx +++ b/src/AIModel/AIFlightPlanCreate.cxx @@ -214,9 +214,9 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight, // and place the model at the location of the gate. if (firstFlight) { - gateId = apt->getDynamics()->getAvailableParking(radius, fltType, + gate = apt->getDynamics()->getAvailableParking(radius, fltType, acType, airline); - if (gateId < 0) { + if (!gate.isValid()) { SG_LOG(SG_AI, SG_WARN, "Could not find parking for a " << acType << " of flight type " << fltType << @@ -245,7 +245,7 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight, } intVec ids; - int runwayId = 0; + PositionedID runwayId = 0; if (gn->getVersion() > 0) { runwayId = gn->findNearestNodeOnRunway(runwayTakeoff); } else { @@ -256,75 +256,74 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight, // fallback mechanism for this. // Starting from gate 0 in this case is a bit of a hack // which requires a more proper solution later on. - delete taxiRoute; - taxiRoute = new FGTaxiRoute; + // delete taxiRoute; + // taxiRoute = new FGTaxiRoute; // Determine which node to start from. - int node = 0; + PositionedID node = 0; // Find out which node to start from - FGParking *park = apt->getDynamics()->getParking(gateId); + FGParking *park = gate.parking(); if (park) { node = park->getPushBackPoint(); - } - - if (node == -1) { - node = gateId; - } - // HAndle case where parking doens't have a node - if ((node == 0) && park) { - if (firstFlight) { - node = gateId; - } else { - node = lastNodeVisited; + if (node == -1) { + node = park->guid(); + } else if (node == 0) { + // HAndle case where parking doens't have a node + if (firstFlight) { + node = park->guid(); + } else { + node = lastNodeVisited; + } } } - - *taxiRoute = gn->findShortestRoute(node, runwayId); + + FGTaxiRoute taxiRoute = gn->findShortestRoute(node, runwayId); intVecIterator i; - if (taxiRoute->empty()) { + if (taxiRoute.empty()) { createDefaultTakeoffTaxi(ac, apt, rwy); return true; } - taxiRoute->first(); + taxiRoute.first(); //bool isPushBackPoint = false; if (firstFlight) { // If this is called during initialization, randomly // skip a number of waypoints to get a more realistic // taxi situation. - int nrWaypointsToSkip = rand() % taxiRoute->size(); + int nrWaypointsToSkip = rand() % taxiRoute.size(); // but make sure we always keep two active waypoints // to prevent a segmentation fault for (int i = 0; i < nrWaypointsToSkip - 3; i++) { - taxiRoute->next(&node); + taxiRoute.next(&node); } - apt->getDynamics()->releaseParking(gateId); + + gate.release(); // free up our gate as required } else { - if (taxiRoute->size() > 1) { - taxiRoute->next(&node); // chop off the first waypoint, because that is already the last of the pushback route + if (taxiRoute.size() > 1) { + taxiRoute.next(&node); // chop off the first waypoint, because that is already the last of the pushback route } } // push each node on the taxi route as a waypoint - int route; + // int route; //cerr << "Building taxi route" << endl; - while (taxiRoute->next(&node, &route)) { + while (taxiRoute.next(&node)) { char buffer[10]; - snprintf(buffer, 10, "%d", node); + snprintf(buffer, 10, "%lld", node); FGTaxiNode *tn = apt->getDynamics()->getGroundNetwork()->findNode(node); FGAIWaypoint *wpt = createOnGround(ac, buffer, tn->geod(), apt->getElevation(), ac->getPerformance()->vTaxi()); - wpt->setRouteIndex(route); + // wpt->setRouteIndex(route); //cerr << "Nodes left " << taxiRoute->nodesLeft() << " "; - if (taxiRoute->nodesLeft() == 1) { + if (taxiRoute.nodesLeft() == 1) { // Note that we actually have hold points in the ground network, but this is just an initial test. //cerr << "Setting departurehold point: " << endl; wpt->setName( wpt->getName() + string("DepartureHold")); } - if (taxiRoute->nodesLeft() == 0) { + if (taxiRoute.nodesLeft() == 0) { wpt->setName(wpt->getName() + string("Accel")); } pushBackWaypoint(wpt); @@ -356,9 +355,8 @@ void FGAIFlightPlan::createDefaultLandingTaxi(FGAIAircraft * ac, ac->getPerformance()->vTaxi()); pushBackWaypoint(wpt); - FGParking* parkPos = aAirport->getDynamics()->getParking(gateId); - if (parkPos) { - wpt = createOnGround(ac, "ENDtaxi", parkPos->geod(), airportElev, + if (gate.isValid()) { + wpt = createOnGround(ac, "ENDtaxi", gate.parking()->geod(), airportElev, ac->getPerformance()->vTaxi()); pushBackWaypoint(wpt); } @@ -370,12 +368,10 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt, const string & acType, const string & airline) { - gateId = apt->getDynamics()->getAvailableParking(radius, fltType, + gate = apt->getDynamics()->getAvailableParking(radius, fltType, acType, airline); - SGGeod lastWptPos = - SGGeod::fromDeg(waypoints.back()->getLongitude(), - waypoints.back()->getLatitude()); + SGGeod lastWptPos = waypoints.back()->getPos(); FGGroundNetwork *gn = apt->getDynamics()->getGroundNetwork(); // Find a route from runway end to parking/gate. @@ -385,7 +381,7 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt, } intVec ids; - int runwayId = 0; + PositionedID runwayId = 0; if (gn->getVersion() == 1) { runwayId = gn->findNearestNodeOnRunway(lastWptPos); } else { @@ -396,34 +392,35 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt, // fallback mechanism for this. // Starting from gate 0 is a bit of a hack... //FGTaxiRoute route; - delete taxiRoute; - taxiRoute = new FGTaxiRoute; - if (gateId >= 0) - *taxiRoute = gn->findShortestRoute(runwayId, gateId); + // delete taxiRoute; + // taxiRoute = new FGTaxiRoute; + FGTaxiRoute taxiRoute; + if (gate.isValid()) + taxiRoute = gn->findShortestRoute(runwayId, gate.parking()->guid()); else - *taxiRoute = gn->findShortestRoute(runwayId, 0); + taxiRoute = gn->findShortestRoute(runwayId, 0); intVecIterator i; - if (taxiRoute->empty()) { + if (taxiRoute.empty()) { createDefaultLandingTaxi(ac, apt); return true; } - int node; - taxiRoute->first(); - int size = taxiRoute->size(); + PositionedID node; + taxiRoute.first(); + int size = taxiRoute.size(); // Omit the last two waypoints, as // those are created by createParking() - int route; + // int route; for (int i = 0; i < size - 2; i++) { - taxiRoute->next(&node, &route); + taxiRoute.next(&node); char buffer[10]; - snprintf(buffer, 10, "%d", node); + snprintf(buffer, 10, "%lld", node); FGTaxiNode *tn = gn->findNode(node); FGAIWaypoint *wpt = createOnGround(ac, buffer, tn->geod(), apt->getElevation(), ac->getPerformance()->vTaxi()); - wpt->setRouteIndex(route); + // wpt->setRouteIndex(route); pushBackWaypoint(wpt); } return true; @@ -638,21 +635,20 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, origin = current; } - double dAlt = 0; // = alt - (apt->getElevation() + 2000); FGTaxiNode * tn = 0; if (apt->getDynamics()->getGroundNetwork()) { int node = apt->getDynamics()->getGroundNetwork()->findNearestNode(refPoint); tn = apt->getDynamics()->getGroundNetwork()->findNode(node); } + if (tn) { - dAlt = alt - ((tn->getElevationFt(apt->getElevation())) + 2000); + dAlt = alt - ((tn->getElevationFt()) + 2000); } else { dAlt = alt - (apt->getElevation() + 2000); } - + double nPoints = 100; - char buffer[16]; // The descent path contains the following phases: @@ -814,7 +810,7 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, //FGTaxiNode * tn = apt->getDynamics()->getGroundNetwork()->findNearestNode(initialTarget); double currentAltitude = 0; if (tn) { - currentAltitude = (tn->getElevationFt(apt->getElevation())) + 2000; + currentAltitude = (tn->getElevationFt()) + 2000; } else { currentAltitude = apt->getElevation() + 2000; } @@ -906,12 +902,12 @@ bool FGAIFlightPlan::createLanding(FGAIAircraft * ac, FGAirport * apt, { double vTouchdown = ac->getPerformance()->vTouchdown(); double vTaxi = ac->getPerformance()->vTaxi(); - double decel = ac->getPerformance()->deceleration() * 1.4; + double decel = ac->getPerformance()->decelerationOnGround(); double vApproach = ac->getPerformance()->vApproach(); - double vTouchdownMetric = (vTouchdown * SG_NM_TO_METER) / 3600; - double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600; - double decelMetric = (decel * SG_NM_TO_METER) / 3600; + double vTouchdownMetric = vTouchdown * SG_KT_TO_MPS; + double vTaxiMetric = vTaxi * SG_KT_TO_MPS; + double decelMetric = decel * SG_KT_TO_MPS; char buffer[12]; FGRunway * rwy = apt->getRunwayByIdent(activeRunway); @@ -996,15 +992,14 @@ bool FGAIFlightPlan::createParking(FGAIAircraft * ac, FGAirport * apt, double aptElev = apt->getElevation(); double vTaxi = ac->getPerformance()->vTaxi(); double vTaxiReduced = vTaxi * (2.0 / 3.0); - FGParking* parking = apt->getDynamics()->getParking(gateId); - if (!parking) { + if (!gate.isValid()) { wpt = createOnGround(ac, "END-Parking", apt->geod(), aptElev, vTaxiReduced); pushBackWaypoint(wpt); - return true; } + FGParking* parking = gate.parking(); double heading = SGMiscd::normalizePeriodic(0, 360, parking->getHeading() + 180.0); double az; // unused SGGeod pos; diff --git a/src/AIModel/AIFlightPlanCreatePushBack.cxx b/src/AIModel/AIFlightPlanCreatePushBack.cxx index 7ebdd7af1..976463ccf 100644 --- a/src/AIModel/AIFlightPlanCreatePushBack.cxx +++ b/src/AIModel/AIFlightPlanCreatePushBack.cxx @@ -49,7 +49,6 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac, double vTaxi = ac->getPerformance()->vTaxi(); double vTaxiBackward = vTaxi * (-2.0/3.0); double vTaxiReduced = vTaxi * (2.0/3.0); - FGTaxiRoute *pushBackRoute; // Active runway can be conditionally set by ATC, so at the start of a new flight, this // must be reset. activeRunway.clear(); @@ -63,9 +62,9 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac, // establish the parking position / gate if required if (firstFlight) { - gateId = dep->getDynamics()->getAvailableParking(radius, fltType, + gate = dep->getDynamics()->getAvailableParking(radius, fltType, aircraftType, airline); - if (gateId < 0) { + if (!gate.isValid()) { SG_LOG(SG_AI, SG_WARN, "Warning: Could not find parking for a " << aircraftType << " of flight type " << fltType << @@ -73,47 +72,47 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac, " at airport " << dep->getId()); return false; } - } else { - dep->getDynamics()->getParking(gateId); } - if (gateId < 0) { + if (!gate.isValid()) { createPushBackFallBack(ac, firstFlight, dep, radius, fltType, aircraftType, airline); return true; } - - FGParking *parking = dep->getDynamics()->getParking(gateId); - int pushBackNode = parking->getPushBackPoint(); - - pushBackRoute = parking->getPushBackRoute(); - if ((pushBackNode > 0) && (pushBackRoute == 0)) { // Load the already established route for this gate - int node, rte; - FGTaxiRoute route; - //cerr << "Creating push-back for " << gateId << " (" << parking->getName() << ") using push-back point " << pushBackNode << endl; - route = dep->getDynamics()->getGroundNetwork()->findShortestRoute(gateId, pushBackNode, false); - parking->setPushBackRoute(std::auto_ptr(new FGTaxiRoute(route))); - - pushBackRoute = parking->getPushBackRoute(); - int size = pushBackRoute->size(); + + FGGroundNetwork* groundNet = dep->getDynamics()->getGroundNetwork(); + FGParking *parking = gate.parking(); + if (parking && parking->getPushBackPoint() > 0) { + FGTaxiRoute route = groundNet->findShortestRoute(parking->guid(), parking->getPushBackPoint(), false); + + int size = route.size(); if (size < 2) { - SG_LOG(SG_AI, SG_ALERT, "Push back route from gate " << gateId << " has only " << size << " nodes."); - SG_LOG(SG_AI, SG_ALERT, "Using " << pushBackNode); + SG_LOG(SG_AI, SG_ALERT, "Push back route from gate " << parking->ident() << " has only " << size << " nodes."); + SG_LOG(SG_AI, SG_ALERT, "Using " << parking->getPushBackPoint()); } - pushBackRoute->first(); - while (pushBackRoute->next(&node, &rte)) + + route.first(); + PositionedID node, previous= 0; + + while (route.next(&node)) { - //FGTaxiNode *tn = apt->getDynamics()->getGroundNetwork()->findSegment(node)->getEnd(); char buffer[10]; - snprintf (buffer, 10, "%d", node); - FGTaxiNode *tn = dep->getDynamics()->getGroundNetwork()->findNode(node); - //ids.pop_back(); - //wpt = new waypoint; + snprintf (buffer, 10, "%lld", node); + FGTaxiNode *tn = groundNet->findNode(node); FGAIWaypoint *wpt = createOnGround(ac, string(buffer), tn->geod(), dep->getElevation(), vTaxiBackward); - - wpt->setRouteIndex(rte); + + if (previous) { + FGTaxiSegment* segment = groundNet->findSegment(previous, node); + wpt->setRouteIndex(segment->getIndex()); + } else { + // not on the route yet, make up a unique segment ID + int x = (int) tn->guid(); + wpt->setRouteIndex(x); + } + pushBackWaypoint(wpt); + previous = node; } // some special considerations for the last point: waypoints.back()->setName(string("PushBackPoint")); @@ -123,36 +122,32 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac, ac->setTaxiClearanceRequest(false); double az2 = 0.0; - //cerr << "Creating final push forward point for gate " << gateId << endl; - FGTaxiNode *tn = dep->getDynamics()->getGroundNetwork()->findNode(gateId); - // there aren't any routes for this parking. - // in cases like these we should flag the gate as being inoperative and return false - if (tn->arcs().empty()) { - SG_LOG(SG_AI, SG_ALERT, "Gate " << gateId << "doesn't seem to have routes associated with it."); - parking->setAvailable(false); - return false; - } - - FGTaxiSegment* pushForwardSegment = tn->arcs().front(); - lastNodeVisited = pushForwardSegment->getEnd()->getIndex(); - double distance = pushForwardSegment->getLength(); + FGTaxiSegment* pushForwardSegment = dep->getDynamics()->getGroundNetwork()->findSegment(parking->guid(), 0); + // there aren't any routes for this parking. + if (!pushForwardSegment) { + SG_LOG(SG_AI, SG_ALERT, "Gate " << parking->ident() << "doesn't seem to have routes associated with it."); + return false; + } - double parkingHeading = parking->getHeading(); - - for (int i = 1; i < 10; i++) { - SGGeod pushForwardPt; - SGGeodesy::direct(parking->geod(), parkingHeading, - ((i / 10.0) * distance), pushForwardPt, az2); - char buffer[16]; - snprintf(buffer, 16, "pushback-%02d", i); - FGAIWaypoint *wpt = createOnGround(ac, string(buffer), pushForwardPt, dep->getElevation(), vTaxiReduced); - - wpt->setRouteIndex(pushForwardSegment->getIndex()); - pushBackWaypoint(wpt); - } - // cerr << "Done " << endl; - waypoints.back()->setName(string("PushBackPoint")); - // cerr << "Done assinging new name" << endl; + lastNodeVisited = pushForwardSegment->getEnd()->getIndex(); + double distance = pushForwardSegment->getLength(); + + double parkingHeading = parking->getHeading(); + + for (int i = 1; i < 10; i++) { + SGGeod pushForwardPt; + SGGeodesy::direct(parking->geod(), parkingHeading, + ((i / 10.0) * distance), pushForwardPt, az2); + char buffer[16]; + snprintf(buffer, 16, "pushback-%02d", i); + FGAIWaypoint *wpt = createOnGround(ac, string(buffer), pushForwardPt, dep->getElevation(), vTaxiReduced); + + wpt->setRouteIndex(pushForwardSegment->getIndex()); + pushBackWaypoint(wpt); + } + + waypoints.back()->setName(string("PushBackPoint")); + // cerr << "Done assinging new name" << endl; } return true; diff --git a/src/AIModel/performancedata.cxx b/src/AIModel/performancedata.cxx index 8e5dfa9f3..80d62dba6 100644 --- a/src/AIModel/performancedata.cxx +++ b/src/AIModel/performancedata.cxx @@ -107,6 +107,11 @@ double PerformanceData::actualSpeed(FGAIAircraft* ac, double tgt_speed, double d return speed; } +double PerformanceData::decelerationOnGround() const +{ + return _deceleration * BRAKE_SETTING; +} + double PerformanceData::actualBankAngle(FGAIAircraft* ac, double tgt_roll, double dt) { // check maximum bank angle if (fabs(tgt_roll) > _maxbank) diff --git a/src/AIModel/performancedata.hxx b/src/AIModel/performancedata.hxx index fd6deb53d..a8aef6400 100644 --- a/src/AIModel/performancedata.hxx +++ b/src/AIModel/performancedata.hxx @@ -46,6 +46,7 @@ public: inline double vTouchdown () { return _vTouchdown; }; inline double vCruise () { return _vCruise; }; + double decelerationOnGround() const; private: double _acceleration; double _deceleration; diff --git a/src/ATC/atc_mgr.cxx b/src/ATC/atc_mgr.cxx index bbfc73311..28639c910 100644 --- a/src/ATC/atc_mgr.cxx +++ b/src/ATC/atc_mgr.cxx @@ -101,12 +101,11 @@ void FGATCManager::init() { FGAirport *apt = FGAirport::findByIdent(airport); if (apt && onGround) {// && !runway.empty()) { FGAirportDynamics* dcs = apt->getDynamics(); - int park_index = dcs->getNrOfParkings() - 1; - //cerr << "found information: " << runway << " " << airport << ": parking = " << parking << endl; fp = new FGAIFlightPlan; - while (park_index >= 0 && dcs->getParkingName(park_index) != parking) park_index--; + ParkingAssignment pk(dcs->getParkingByName(parking)); + // No valid parking location, so either at the runway or at a random location. - if (parking.empty() || (park_index < 0)) { + if (!pk.isValid()) { if (!runway.empty()) { controller = apt->getDynamics()->getTowerController(); int stationFreq = apt->getDynamics()->getTowerFrequency(2); @@ -134,12 +133,11 @@ void FGATCManager::init() { leg = 1; //double, lat, lon, head; // Unused variables; //int getId = apt->getDynamics()->getParking(gateId, &lat, &lon, &head); - FGParking* parking = dcs->getParking(park_index); - aircraftRadius = parking->getRadius(); - string fltType = parking->getType(); // gate / ramp, ga, etc etc. + aircraftRadius = pk.parking()->getRadius(); + string fltType = pk.parking()->getType(); // gate / ramp, ga, etc etc. string aircraftType; // Unused. string airline; // Currently used for gate selection, but a fallback mechanism will apply when not specified. - fp->setGate(park_index); + fp->setGate(pk); if (!(fp->createPushBack(&ai_ac, false, apt, diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index c71dd034d..7f1596a00 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -1363,7 +1363,7 @@ void FGStartupController::render(bool visible) } else { elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER); } - double elevationEnd = segment->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationEnd = segment->getEnd()->getElevationM(); if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) { SGGeod center2 = end; center2.setElevationM(SG_MAX_ELEVATION_M); @@ -1425,8 +1425,8 @@ void FGStartupController::render(bool visible) obj_trans->setDataVariance(osg::Object::STATIC); FGTaxiSegment *segment = parent->getGroundNetwork()->findSegment(k); - double elevationStart = segment->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); - double elevationEnd = segment->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationStart = segment->getStart()->getElevationM(); + double elevationEnd = segment->getEnd ()->getElevationM(); if ((elevationStart == 0) || (elevationStart == parent->getElevation())) { SGGeod center2 = segment->getStart()->geod(); center2.setElevationM(SG_MAX_ELEVATION_M); diff --git a/src/Airports/dynamicloader.cxx b/src/Airports/dynamicloader.cxx index b79030a22..bf3eeed70 100644 --- a/src/Airports/dynamicloader.cxx +++ b/src/Airports/dynamicloader.cxx @@ -19,9 +19,14 @@ #include #include // for strcmp +#include #include "dynamicloader.hxx" +#include +#include +#include + /***************************************************************************** * Helper function for parsing position string ****************************************************************************/ @@ -53,8 +58,25 @@ void FGAirportDynamicsXMLLoader::startXML () { //cout << "FGAirportDynamicsLoader::Start XML" << endl; } -void FGAirportDynamicsXMLLoader::endXML () { - //cout << "End XML" << endl; +void FGAirportDynamicsXMLLoader::endXML () +{ + std::map::iterator it; + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + + for (it = _parkingPushbacks.begin(); it != _parkingPushbacks.end(); ++it) { + std::map::iterator j = _idMap.find(it->second); + if (j == _idMap.end()) { + SG_LOG(SG_GENERAL, SG_WARN, "bad groundnet, no node for index:" << it->first); + continue; + } + + cache->setParkingPushBackRoute(it->first, j->second); + } + + BOOST_FOREACH(PositionedID id, _unreferencedNodes) { + SG_LOG(SG_GENERAL, SG_WARN, "unreferenced groundnet node:" << id); + } + } void FGAirportDynamicsXMLLoader::startParking(const XMLAttributes &atts) @@ -100,10 +122,14 @@ void FGAirportDynamicsXMLLoader::startParking(const XMLAttributes &atts) SGGeod pos(SGGeod::fromDeg(processPosition(lon), processPosition(lat))); - FGParking* pk = new FGParking(0, index, pos, heading, radius, - gateName + gateNumber, type, airlineCodes); - pk->setPushBackPoint(pushBackRoute); - _dynamics->addParking(pk); + PositionedID guid = flightgear::NavDataCache::instance()->insertParking(gateName + gateNumber, pos, + _dynamics->parent()->guid(), + heading, radius, type, airlineCodes); + if (pushBackRoute > 0) { + _parkingPushbacks[guid] = pushBackRoute; + } + + _idMap[index] = guid; } void FGAirportDynamicsXMLLoader::startNode(const XMLAttributes &atts) @@ -140,9 +166,15 @@ void FGAirportDynamicsXMLLoader::startNode(const XMLAttributes &atts) } } + if (_idMap.find(index) != _idMap.end()) { + SG_LOG(SG_GENERAL, SG_WARN, "duplicate ground-net index:" << index); + } + SGGeod pos(SGGeod::fromDeg(processPosition(lon), processPosition(lat))); - FGTaxiNode* taxiNode = new FGTaxiNode(0, index, pos, onRunway, holdPointType); - _dynamics->getGroundNetwork()->addNode(taxiNode); + PositionedID guid = flightgear::NavDataCache::instance()->insertTaxiNode(pos, + _dynamics->parent()->guid(), holdPointType, onRunway); + _idMap[index] = guid; + _unreferencedNodes.insert(guid); } void FGAirportDynamicsXMLLoader::startArc(const XMLAttributes &atts) @@ -161,7 +193,22 @@ void FGAirportDynamicsXMLLoader::startArc(const XMLAttributes &atts) isPushBackRoute = std::atoi(atts.getValue(i)) != 0; } - _dynamics->getGroundNetwork()->addSegment(new FGTaxiSegment(begin, end, isPushBackRoute)); + IntPair e(begin, end); + if (_arcSet.find(e) != _arcSet.end()) { + SG_LOG(SG_GENERAL, SG_WARN, _dynamics->parent()->ident() << " ground-net: skipping duplicate edge:" << begin << "->" << end); + return; + } + + _arcSet.insert(e); + flightgear::NavDataCache::instance()->insertGroundnetEdge(_dynamics->parent()->guid(), + _idMap[begin], _idMap[end]); + + _unreferencedNodes.erase(_idMap[begin]); + _unreferencedNodes.erase(_idMap[end]); + + if (isPushBackRoute) { + flightgear::NavDataCache::instance()->markGroundnetAsPushback(_idMap[end]); + } } void FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttributes &atts) diff --git a/src/Airports/dynamicloader.hxx b/src/Airports/dynamicloader.hxx index 0584d06ac..b204e9bf1 100644 --- a/src/Airports/dynamicloader.hxx +++ b/src/Airports/dynamicloader.hxx @@ -19,6 +19,7 @@ #include #include "dynamics.hxx" +#include class FGAirportDynamicsXMLLoader : public XMLVisitor { public: @@ -41,6 +42,20 @@ private: FGAirportDynamics* _dynamics; string value; + + // map from local (groundnet.xml) to global (nav-cache) IDs for nodes + std::map _idMap; + + // data integrity - watch for unreferenced nodes and duplicated edges + typedef std::pair IntPair; + std::set _arcSet; + + std::set _unreferencedNodes; + + // map from allocated parking position to its local push-back node + // used to defer binding the push-back node until we've processed + // all nodes + std::map _parkingPushbacks; }; #endif diff --git a/src/Airports/dynamics.cxx b/src/Airports/dynamics.cxx index dde198356..d505d580c 100644 --- a/src/Airports/dynamics.cxx +++ b/src/Airports/dynamics.cxx @@ -40,6 +40,7 @@ #include
#include #include +#include #include "simple.hxx" #include "dynamics.hxx" @@ -49,6 +50,105 @@ using std::vector; using std::sort; using std::random_shuffle; +class ParkingAssignment::ParkingAssignmentPrivate +{ +public: + ParkingAssignmentPrivate(FGParking* pk, FGAirport* apt) : + refCount(0), + parking(pk), + airport(apt) + { + assert(pk); + assert(apt); + retain(); // initial count of 1 + } + + ~ParkingAssignmentPrivate() + { + airport->getDynamics()->releaseParking(parking->guid()); + } + + void release() + { + if ((--refCount) == 0) { + delete this; + } + } + + void retain() + { + ++refCount; + } + + unsigned int refCount; + SGSharedPtr parking; + SGSharedPtr airport; +}; + +ParkingAssignment::ParkingAssignment() : + _sharedData(NULL) +{ +} + +ParkingAssignment::~ParkingAssignment() +{ + if (_sharedData) { + _sharedData->release(); + } +} + +ParkingAssignment::ParkingAssignment(FGParking* pk, FGAirport* apt) : + _sharedData(NULL) +{ + if (pk) { + _sharedData = new ParkingAssignmentPrivate(pk, apt); + } +} + +ParkingAssignment::ParkingAssignment(const ParkingAssignment& aOther) : + _sharedData(aOther._sharedData) +{ + if (_sharedData) { + _sharedData->retain(); + } +} + +void ParkingAssignment::operator=(const ParkingAssignment& aOther) +{ + if (_sharedData == aOther._sharedData) { + return; // self-assignment, special case + } + + if (_sharedData) { + _sharedData->release(); + } + + _sharedData = aOther._sharedData; + if (_sharedData) { + _sharedData->retain(); + } +} + +void ParkingAssignment::release() +{ + if (_sharedData) { + _sharedData->release(); + _sharedData = NULL; + } +} + +bool ParkingAssignment::isValid() const +{ + return (_sharedData != NULL); +} + +FGParking* ParkingAssignment::parking() const +{ + return _sharedData ? _sharedData->parking.ptr() : NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + FGAirportDynamics::FGAirportDynamics(FGAirport * ap): _ap(ap), rwyPrefs(ap), startupController (this), @@ -70,96 +170,68 @@ FGAirportDynamics::~FGAirportDynamics() // Initialization required after XMLRead void FGAirportDynamics::init() { - // This may seem a bit weird to first randomly shuffle the parkings - // and then sort them again. However, parkings are sorted here by ascending - // radius. Since many parkings have similar radii, with each radius class they will - // still be allocated relatively systematically. Randomizing prior to sorting will - // prevent any initial orderings to be destroyed, leading (hopefully) to a more - // naturalistic gate assignment. - random_shuffle(parkings.begin(), parkings.end()); - sort(parkings.begin(), parkings.end()); - // add the gate positions to the ground network. - groundNetwork.setParent(_ap); - groundNetwork.addNodes(&parkings); - groundNetwork.init(); + groundNetwork.init(_ap); groundNetwork.setTowerController(&towerController); } -int FGAirportDynamics::innerGetAvailableParking(double radius, const string & flType, - const string & acType, +FGParking* FGAirportDynamics::innerGetAvailableParking(double radius, const string & flType, const string & airline, bool skipEmptyAirlineCode) { - BOOST_FOREACH(FGParking* i, parkings) { - // Taken by another aircraft, or no airline codes - if (!i->isAvailable()) { + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + BOOST_FOREACH(PositionedID pk, cache->findAirportParking(_ap->guid(), flType, radius)) { + if (!isParkingAvailable(pk)) { continue; } - if (skipEmptyAirlineCode && i->getCodes().empty()) { + FGParking* parking = getParking(pk); + if (skipEmptyAirlineCode && parking->getCodes().empty()) { continue; } - // check airline codes match - if (!airline.empty() && !i->getCodes().empty()) { - if (i->getCodes().find(airline, 0) == string::npos) { + if (!airline.empty() && !parking->getCodes().empty()) { + if (parking->getCodes().find(airline, 0) == string::npos) { continue; } } - // Type doesn't match - if (i->getType() != flType) { - continue; - } - // too small - if (i->getRadius() < radius) { - continue; - } - - i->setAvailable(false); - return i->getIndex(); + setParkingAvailable(pk, false); + return parking; } - return -1; + return NULL; } -int FGAirportDynamics::getAvailableParking(double radius, const string & flType, +ParkingAssignment FGAirportDynamics::getAvailableParking(double radius, const string & flType, const string & acType, const string & airline) { - if (parkings.empty()) { - return -1; - } + SG_UNUSED(acType); // sadly not used at the moment // most exact seach - airline codes must be present and match - int result = innerGetAvailableParking(radius, flType, acType, airline, true); - if (result >= 0) { - return result; + FGParking* result = innerGetAvailableParking(radius, flType, airline, true); + if (result) { + return ParkingAssignment(result, _ap); } // more tolerant - gates with empty airline codes are permitted - result = innerGetAvailableParking(radius, flType, acType, airline, false); - if (result >= 0) { - return result; + result = innerGetAvailableParking(radius, flType, airline, false); + if (result) { + return ParkingAssignment(result, _ap); } // fallback - ignore the airline code entirely - return innerGetAvailableParking(radius, flType, acType, string(), false); + result = innerGetAvailableParking(radius, flType, string(), false); + return result ? ParkingAssignment(result, _ap) : ParkingAssignment(); } -FGParking *FGAirportDynamics::getParking(int id) +FGParking *FGAirportDynamics::getParking(PositionedID id) const { - BOOST_FOREACH(FGParking* i, parkings) { - if (id == i->getIndex()) { - return i; - } - } - - return NULL; + return static_cast(flightgear::NavDataCache::instance()->loadById(id)); } -string FGAirportDynamics::getParkingName(int id) +string FGAirportDynamics::getParkingName(PositionedID id) const { FGParking* p = getParking(id); if (p) { @@ -169,25 +241,38 @@ string FGAirportDynamics::getParkingName(int id) return string(); } -int FGAirportDynamics::findParkingByName(const std::string& name) const +ParkingAssignment FGAirportDynamics::getParkingByName(const std::string& name) const { - BOOST_FOREACH(FGParking* i, parkings) { - if (name == i->getName()) { - return i->getIndex(); - } + PositionedID guid = flightgear::NavDataCache::instance()->airportItemWithIdent(parent()->guid(), FGPositioned::PARKING, name); + if (guid == 0) { + return ParkingAssignment(); } + + return ParkingAssignment(getParking(guid), _ap); +} - return -1; +void FGAirportDynamics::setParkingAvailable(PositionedID guid, bool available) +{ + if (available) { + releaseParking(guid); + } else { + occupiedParkings.insert(guid); + } } -void FGAirportDynamics::releaseParking(int id) +bool FGAirportDynamics::isParkingAvailable(PositionedID parking) const { - if (id >= 0) { - FGParking* parking = getParking(id); - if (parking) { - parking->setAvailable(true); - } - } + return (occupiedParkings.find(parking) == occupiedParkings.end()); +} + +void FGAirportDynamics::releaseParking(PositionedID id) +{ + ParkingSet::iterator it = occupiedParkings.find(id); + if (it == occupiedParkings.end()) { + return; + } + + occupiedParkings.erase(it); } void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref) @@ -373,11 +458,6 @@ string FGAirportDynamics::chooseRunwayFallback() return rwy->ident(); } -void FGAirportDynamics::addParking(FGParking* park) -{ - parkings.push_back(park); -} - double FGAirportDynamics::getElevation() const { return _ap->getElevation(); diff --git a/src/Airports/dynamics.hxx b/src/Airports/dynamics.hxx index 33481b12c..007c8a780 100644 --- a/src/Airports/dynamics.hxx +++ b/src/Airports/dynamics.hxx @@ -22,6 +22,8 @@ #ifndef _AIRPORT_DYNAMICS_HXX_ #define _AIRPORT_DYNAMICS_HXX_ +#include + #include #include "parking.hxx" #include "groundnetwork.hxx" @@ -31,12 +33,38 @@ class FGAirport; class FGEnvironment; +class ParkingAssignment +{ +public: + ParkingAssignment(); + ~ParkingAssignment(); + +// create a parking assignment (and mark it as unavailable) + ParkingAssignment(FGParking* pk, FGAirport* apt); + + ParkingAssignment(const ParkingAssignment& aOther); + void operator=(const ParkingAssignment& aOther); + + bool isValid() const; + FGParking* parking() const; + + void release(); +private: + void clear(); + + class ParkingAssignmentPrivate; + ParkingAssignmentPrivate* _sharedData; +}; + class FGAirportDynamics { private: FGAirport* _ap; - FGParkingVec parkings; + typedef std::set ParkingSet; + // if a parking item is in this set, it is occupied + ParkingSet occupiedParkings; + FGRunwayPreference rwyPrefs; FGStartupController startupController; FGGroundNetwork groundNetwork; @@ -63,8 +91,8 @@ private: bool innerGetActiveRunway(const std::string &trafficType, int action, std::string &runway, double heading); std::string chooseRwyByHeading(stringVec rwys, double heading); - int innerGetAvailableParking(double radius, const std::string & flType, - const std::string & acType, const std::string & airline, + FGParking* innerGetAvailableParking(double radius, const std::string & flType, + const std::string & airline, bool skipEmptyAirlineCode); public: FGAirportDynamics(FGAirport* ap); @@ -98,28 +126,28 @@ public: { return _ap; } void getActiveRunway(const string& trafficType, int action, string& runway, double heading); - - void addParking(FGParking* park); /** * retrieve an available parking by GateID, or -1 if no suitable * parking location could be found. */ - int getAvailableParking(double radius, const std::string& fltype, + ParkingAssignment getAvailableParking(double radius, const std::string& fltype, const std::string& acType, const std::string& airline); - FGParking *getParking(int i); - void releaseParking(int id); - std::string getParkingName(int i); - int getNrOfParkings() { - return parkings.size(); - }; + void setParkingAvailable(PositionedID guid, bool available); + + bool isParkingAvailable(PositionedID parking) const; + + FGParking *getParking(PositionedID i) const; + void releaseParking(PositionedID id); + std::string getParkingName(PositionedID i) const; /** * Find a parking gate index by name. Note names are often not unique - * in our data, so will return the first match. + * in our data, so will return the first match. If the parking is found, + * it will be marked as in-use (unavailable) */ - int findParkingByName(const std::string& name) const; + ParkingAssignment getParkingByName(const std::string& name) const; // ATC related functions. FGStartupController *getStartupController() { diff --git a/src/Airports/gnnode.cxx b/src/Airports/gnnode.cxx index 85fc93022..12759bc2c 100644 --- a/src/Airports/gnnode.cxx +++ b/src/Airports/gnnode.cxx @@ -4,16 +4,18 @@ #include "groundnetwork.hxx" +#include #include
#include +using namespace flightgear; + /************************************************************************** * FGTaxiNode *************************************************************************/ -FGTaxiNode::FGTaxiNode(PositionedID aGuid, int index, const SGGeod& pos, bool aOnRunway, int aHoldType) : +FGTaxiNode::FGTaxiNode(PositionedID aGuid, const SGGeod& pos, bool aOnRunway, int aHoldType) : FGPositioned(aGuid, FGPositioned::PARKING, "", pos), - index(index), isOnRunway(aOnRunway), holdType(aHoldType) { @@ -29,35 +31,24 @@ void FGTaxiNode::setElevation(double val) // ignored for the moment } -double FGTaxiNode::getElevationFt(double refelev) +double FGTaxiNode::getElevationFt() { -#if 0 - double elevF = elevation(); - double elevationEnd = 0; - if ((elevF == 0) || (elevF == refelev)) { - SGGeod center2 = mPosition; - FGScenery * local_scenery = globals->get_scenery(); - center2.setElevationM(SG_MAX_ELEVATION_M); - if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - geod.setElevationM(elevationEnd); - } + if (mPosition.getElevationFt() == 0.0) { + SGGeod center2 = mPosition; + FGScenery* local_scenery = globals->get_scenery(); + center2.setElevationM(SG_MAX_ELEVATION_M); + double elevationEnd = -100; + if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { + + mPosition.setElevationM(elevationEnd); + NavDataCache::instance()->updatePosition(guid(), mPosition); } -#endif + } + return mPosition.getElevationFt(); } -double FGTaxiNode::getElevationM(double refelev) +double FGTaxiNode::getElevationM() { - return geod().getElevationM(); -} - -FGTaxiSegment* FGTaxiNode::getArcTo(FGTaxiNode* aEnd) const -{ - BOOST_FOREACH(FGTaxiSegment* arc, next) { - if (arc->getEnd() == aEnd) { - return arc; - } - } - - return NULL; + return getElevationFt() * SG_FEET_TO_METER; } diff --git a/src/Airports/gnnode.hxx b/src/Airports/gnnode.hxx index 5902badfb..2ade6673d 100644 --- a/src/Airports/gnnode.hxx +++ b/src/Airports/gnnode.hxx @@ -16,70 +16,29 @@ #ifndef _GN_NODE_HXX_ #define _GN_NODE_HXX_ -#include -#include - #include #include #include -class FGTaxiSegment; - -typedef std::vector FGTaxiSegmentVector; -typedef FGTaxiSegmentVector::iterator FGTaxiSegmentVectorIterator; - -bool sortByHeadingDiff(FGTaxiSegment *a, FGTaxiSegment *b); -bool sortByLength (FGTaxiSegment *a, FGTaxiSegment *b); - class FGTaxiNode : public FGPositioned { protected: - int index; - bool isOnRunway; int holdType; - FGTaxiSegmentVector next; // a vector of pointers to all the segments leaving from this node - - // used in way finding - should really move to a dynamic struct - double pathScore; - FGTaxiNode* previousNode; - FGTaxiSegment* previousSeg; - public: - FGTaxiNode(PositionedID aGuid, int index, const SGGeod& pos, bool aOnRunway, int aHoldType); + FGTaxiNode(PositionedID aGuid, const SGGeod& pos, bool aOnRunway, int aHoldType); virtual ~FGTaxiNode(); void setElevation(double val); - void addSegment(FGTaxiSegment *segment) { next.push_back(segment); }; - - void setPathScore (double val) { pathScore = val; }; - void setPreviousNode(FGTaxiNode *val) { previousNode = val; }; - void setPreviousSeg (FGTaxiSegment *val) { previousSeg = val; }; - FGTaxiNode *getPreviousNode() { return previousNode; }; - FGTaxiSegment *getPreviousSegment() { return previousSeg; }; - - double getPathScore() { return pathScore; }; - - double getElevationM (double refelev); - double getElevationFt(double refelev); + double getElevationM (); + double getElevationFt(); - int getIndex() const { return index; }; + PositionedID getIndex() const { return guid(); }; int getHoldPointType() const { return holdType; }; bool getIsOnRunway() const { return isOnRunway; }; - - const FGTaxiSegmentVector& arcs() const - { return next; } - - /// find the arg which leads from this node to another. - /// returns NULL if no such arc exists. - FGTaxiSegment* getArcTo(FGTaxiNode* aEnd) const; - - bool operator<(const FGTaxiNode &other) const { return index < other.index; }; - - }; typedef SGSharedPtr FGTaxiNode_ptr; diff --git a/src/Airports/groundnetwork.cxx b/src/Airports/groundnetwork.cxx index 3c65c8e48..fe4d2e75b 100644 --- a/src/Airports/groundnetwork.cxx +++ b/src/Airports/groundnetwork.cxx @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include #include #include @@ -48,6 +50,7 @@ #include #include #include +#include #include @@ -56,52 +59,50 @@ #include "groundnetwork.hxx" using std::string; +using flightgear::NavDataCache; /*************************************************************************** * FGTaxiSegment **************************************************************************/ -FGTaxiSegment::FGTaxiSegment(int aStart, int aEnd, bool isPushBack) : +FGTaxiSegment::FGTaxiSegment(PositionedID aStart, PositionedID aEnd) : startNode(aStart), endNode(aEnd), - length(0), - heading(0), isActive(0), - isPushBackRoute(isPushBack), - start(0), - end(0), index(0), oppositeDirection(0) { }; -bool FGTaxiSegment::bindToNodes(const IndexTaxiNodeMap& nodes) +SGGeod FGTaxiSegment::getCenter() const { - IndexTaxiNodeMap::const_iterator it = nodes.find(startNode); - if (it == nodes.end()) { - return false; - } - - start = it->second; - - it = nodes.find(endNode); - if (it == nodes.end()) { - return false; - } - - end = it->second; - - start->addSegment(this); - double az2; + FGTaxiNode* start(getStart()), *end(getEnd()); + double heading, length, az2; SGGeodesy::inverse(start->geod(), end->geod(), heading, az2, length); - return true; + return SGGeodesy::direct(start->geod(), heading, length * 0.5); } -SGGeod FGTaxiSegment::getCenter() const +FGTaxiNode* FGTaxiSegment::getEnd() const { - return SGGeodesy::direct(start->geod(), heading, length * 0.5); + return static_cast(NavDataCache::instance()->loadById(endNode)); +} + +FGTaxiNode* FGTaxiSegment::getStart() const +{ + return static_cast(NavDataCache::instance()->loadById(startNode)); +} + +double FGTaxiSegment::getLength() const +{ + return dist(getStart()->cart(), getEnd()->cart()); +} + +double FGTaxiSegment::getHeading() const +{ + return SGGeodesy::courseDeg(getStart()->geod(), getEnd()->geod()); } + void FGTaxiSegment::block(int id, time_t blockTime, time_t now) { BlockListIterator i = blockTimes.begin(); @@ -144,81 +145,17 @@ void FGTaxiSegment::unblock(time_t now) /*************************************************************************** * FGTaxiRoute **************************************************************************/ -bool FGTaxiRoute::next(int *nde) +bool FGTaxiRoute::next(PositionedID *nde) { - //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++) - // cerr << "FGTaxiRoute contains : " << *(i) << endl; - //cerr << "Offset from end: " << nodes.end() - currNode << endl; - //if (currNode != nodes.end()) - // cerr << "true" << endl; - //else - // cerr << "false" << endl; - //if (nodes.size() != (routes.size()) +1) - // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl; - if (currNode == nodes.end()) return false; + *nde = *(currNode); - if (currNode != nodes.begin()) // make sure route corresponds to the end node - currRoute++; - currNode++; - return true; -}; -bool FGTaxiRoute::next(int *nde, int *rte) -{ - //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++) - // cerr << "FGTaxiRoute contains : " << *(i) << endl; - //cerr << "Offset from end: " << nodes.end() - currNode << endl; - //if (currNode != nodes.end()) - // cerr << "true" << endl; - //else - // cerr << "false" << endl; - if (nodes.size() != (routes.size()) + 1) { - SG_LOG(SG_GENERAL, SG_ALERT, - "ALERT: Misconfigured TaxiRoute : " << nodes. - size() << " " << routes.size()); - throw sg_range_exception("misconfigured taxi route"); - } - if (currNode == nodes.end()) - return false; - *nde = *(currNode); - //*rte = *(currRoute); - if (currNode != nodes.begin()) // Make sure route corresponds to the end node - { - *rte = *(currRoute); - currRoute++; - } else { - // If currNode points to the first node, this means the aircraft is not on the taxi node - // yet. Make sure to return a unique identifyer in this situation though, because otherwise - // the speed adjust AI code may be unable to resolve whether two aircraft are on the same - // taxi route or not. the negative of the preceding route seems a logical choice, as it is - // unique for any starting location. - // Note that this is probably just a temporary fix until I get Parking / tower control working. - *rte = -1 * *(currRoute); - } currNode++; return true; }; - -void FGTaxiRoute::rewind(int route) -{ - int currPoint; - int currRoute; - first(); - do { - if (!(next(&currPoint, &currRoute))) { - SG_LOG(SG_GENERAL, SG_ALERT, - "Error in rewinding TaxiRoute: current" << currRoute << - " goal " << route); - } - } while (currRoute != route); -} - - - - /*************************************************************************** * FGGroundNetwork() **************************************************************************/ @@ -232,7 +169,6 @@ FGGroundNetwork::FGGroundNetwork() : parent(NULL) { hasNetwork = false; - foundRoute = false; totalDistance = 0; maxDistance = 0; //maxDepth = 1000; @@ -253,52 +189,16 @@ FGGroundNetwork::~FGGroundNetwork() // When I fix FGPositioned lifetimes (unloading-at-runtime support), this // will need to be re-visited so it can run safely during shutdown. #if 0 - //cerr << "Running Groundnetwork Destructor " << endl; - bool saveData = false; - ofstream cachefile; - if (fgGetBool("/sim/ai/groundnet-cache")) { - SGPath cacheData(globals->get_fg_home()); - cacheData.append("ai"); - string airport = parent->getId(); - - if ((airport) != "") { - char buffer[128]; - ::snprintf(buffer, 128, "%c/%c/%c/", - airport[0], airport[1], airport[2]); - cacheData.append(buffer); - if (!cacheData.exists()) { - cacheData.create_dir(0777); - } - cacheData.append(airport + "-groundnet-cache.txt"); - cachefile.open(cacheData.str().c_str()); - saveData = true; - } - } - cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl; - for (FGTaxiNodeVectorIterator node = nodes.begin(); - node != nodes.end(); node++) { - if (saveData) { - cachefile << (*node)->getIndex () << " " - << (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " " - << endl; - } - delete(*node); - } - nodes.clear(); - pushBackNodes.clear(); - for (FGTaxiSegmentVectorIterator seg = segments.begin(); - seg != segments.end(); seg++) { - delete(*seg); - } - segments.clear(); - if (saveData) { - cachefile.close(); - } + saveElevationCache(); #endif + BOOST_FOREACH(FGTaxiSegment* seg, segments) { + delete seg; + } } -void FGGroundNetwork::saveElevationCache() { - //cerr << "Running Groundnetwork Destructor " << endl; +void FGGroundNetwork::saveElevationCache() +{ +#if 0 bool saveData = false; ofstream cachefile; if (fgGetBool("/sim/ai/groundnet-cache")) { @@ -331,59 +231,38 @@ void FGGroundNetwork::saveElevationCache() { if (saveData) { cachefile.close(); } +#endif } -void FGGroundNetwork::addSegment(FGTaxiSegment* seg) -{ - segments.push_back(seg); -} - -void FGGroundNetwork::addNode(FGTaxiNode* node) -{ - assert(node); - IndexTaxiNodeMap::iterator it = nodes.find(node->getIndex()); - if (it != nodes.end()) { - throw sg_range_exception(); - } - - nodes.insert(it, std::make_pair(node->getIndex(), node)); -} - -void FGGroundNetwork::addNodes(FGParkingVec * parkings) -{ - BOOST_FOREACH(FGParking* parking, *parkings) { - addNode(parking); - } -} - -void FGGroundNetwork::init() +void FGGroundNetwork::init(FGAirport* pr) { if (networkInitialized) { FGATCController::init(); //cerr << "FGground network already initialized" << endl; return; } + + parent = pr; + assert(parent); hasNetwork = true; nextSave = 0; int index = 1; - // bind segments to nodes + loadSegments(); + + // establish pairing of segments BOOST_FOREACH(FGTaxiSegment* segment, segments) { - if (!segment->bindToNodes(nodes)) { - SG_LOG(SG_GENERAL, SG_ALERT, "unable to bind taxiway segment"); - } - segment->setIndex(index++); - if (segment->isPushBack()) { - pushBackNodes.push_back(segment->getEnd()); + + if (segment->oppositeDirection) { + continue; // already establish } - } - - // establish pairing of segments - BOOST_FOREACH(FGTaxiSegment* segment, segments) { - FGTaxiSegment* opp = segment->getEnd()->getArcTo(segment->getStart()); + + FGTaxiSegment* opp = findSegment(segment->endNode, segment->startNode); if (opp) { - segment->setOpposite(opp); + assert(opp->oppositeDirection == NULL); + segment->oppositeDirection = opp; + opp->oppositeDirection = segment; } } @@ -394,6 +273,18 @@ void FGGroundNetwork::init() networkInitialized = true; } +void FGGroundNetwork::loadSegments() +{ + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); +// iterate over all ground-net nodes in this airport + BOOST_FOREACH(PositionedID node, cache->groundNetNodes(parent->guid(), false)) { + // find all segments leaving the node + BOOST_FOREACH(PositionedID end, cache->groundNetEdgesFrom(node, false)) { + segments.push_back(new FGTaxiSegment(node, end)); + } + } +} + void FGGroundNetwork::parseCache() { SGPath cacheData(globals->get_fg_home()); @@ -403,7 +294,7 @@ void FGGroundNetwork::parseCache() if (airport.empty()) { return; } - +#if 0 char buffer[128]; ::snprintf(buffer, 128, "%c/%c/%c/", airport[0], airport[1], airport[2]); @@ -437,76 +328,29 @@ void FGGroundNetwork::parseCache() } } } +#endif } -int FGGroundNetwork::findNearestNode(const SGGeod & aGeod) +int FGGroundNetwork::findNearestNode(const SGGeod & aGeod) const { - double minDist = HUGE_VAL; - int index = -1; - - IndexTaxiNodeMap::iterator i; - for (i = nodes.begin(); i != nodes.end(); i++) { - double d = SGGeodesy::distanceM(aGeod, i->second->geod()); - if (d < minDist) { - minDist = d; - index = i->first; - } - } - - return index; + const bool onRunway = false; + return NavDataCache::instance()->findGroundNetNode(parent->guid(), aGeod, onRunway); } -int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway) +int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway) const { - double minDist = HUGE_VAL; - int index = -1; - - IndexTaxiNodeMap::iterator i; - for (i = nodes.begin(); i != nodes.end(); i++) { - if (!i->second->getIsOnRunway()) { - continue; - } - // check point lies on the runway - i.e that course from aGeod to the - // runway end, matches the runway heading - if (aRunway) { - double course = SGGeodesy::courseDeg(i->second->geod(), aRunway->end()); - double headingDiff = course - aRunway->headingDeg(); - SG_NORMALIZE_RANGE(headingDiff, -180.0, 180.0); - if (fabs(headingDiff) > 3.0) { // 3 degrees tolerance - continue; - } - } - - double d = SGGeodesy::distanceM(aGeod, i->second->geod()); - if (d < minDist) { - minDist = d; - index = i->first; - } - } - - return index; + const bool onRunway = true; + return NavDataCache::instance()->findGroundNetNode(parent->guid(), aGeod, onRunway, aRunway); } -FGTaxiNode* FGGroundNetwork::findNode(unsigned int idx) -{ - IndexTaxiNodeMap::iterator i = nodes.find(idx); - if (i == nodes.end()) { - return NULL; - } - - return i->second; +FGTaxiNode* FGGroundNetwork::findNode(PositionedID idx) const +{ + + return static_cast(NavDataCache::instance()->loadById(idx)); } -FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) -{ /* - for (FGTaxiSegmentVectorIterator - itr = segments.begin(); - itr != segments.end(); itr++) - { - if (itr->getIndex() == idx) - return itr->getAddress(); - } - */ +FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) const +{ if ((idx > 0) && (idx <= segments.size())) return segments[idx - 1]; else { @@ -515,34 +359,57 @@ FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) } } +FGTaxiSegment* FGGroundNetwork::findSegment(PositionedID from, PositionedID to) const +{ + if (from == 0) { + return NULL; + } + + // completely boring linear search of segments. Can be improved if/when + // this ever becomes a hot-spot + BOOST_FOREACH(FGTaxiSegment* seg, segments) { + if (seg->startNode != from) { + continue; + } + + if ((to == 0) || (seg->endNode == to)) { + return seg; + } + } + + return NULL; // not found +} + +static int edgePenalty(FGTaxiNode* tn) +{ + return (tn->type() == FGPositioned::PARKING ? 10000 : 0) + + (tn->getIsOnRunway() ? 1000 : 0); +} + +class ShortestPathData +{ +public: + ShortestPathData() : + score(HUGE_VAL) + {} + + double score; + FGTaxiNode_ptr previousNode; +}; -FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, +FGTaxiRoute FGGroundNetwork::findShortestRoute(PositionedID start, PositionedID end, bool fullSearch) { //implements Dijkstra's algorithm to find shortest distance route from start to end //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm - - //double INFINITE = 100000000000.0; - // initialize scoring values - int nParkings = parent->getDynamics()->getNrOfParkings(); FGTaxiNodeVector unvisited; + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + std::map searchData; - if (fullSearch) { - // create vector from map values - IndexTaxiNodeMap::iterator i; - for (i = nodes.begin(); i != nodes.end(); i++) { - unvisited.push_back(i->second); - } - } else { - unvisited = pushBackNodes; + BOOST_FOREACH(PositionedID n, cache->groundNetNodes(parent->guid(), !fullSearch)) { + unvisited.push_back(findNode(n)); } - BOOST_FOREACH(FGTaxiNode* node, unvisited) { - node->setPathScore(HUGE_VAL); //infinity by all practical means - node->setPreviousNode(0); // - node->setPreviousSeg(0); // - } - FGTaxiNode *firstNode = findNode(start); if (!firstNode) { @@ -551,7 +418,7 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, << " at " << ((parent) ? parent->getId() : "")); return FGTaxiRoute(); } - firstNode->setPathScore(0); + searchData[firstNode].score = 0.0; FGTaxiNode *lastNode = findNode(end); if (!lastNode) @@ -565,11 +432,11 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, while (!unvisited.empty()) { FGTaxiNode *best = unvisited.front(); BOOST_FOREACH(FGTaxiNode* i, unvisited) { - if (i->getPathScore() < best->getPathScore()) { + if (searchData[i].score < searchData[best].score) { best = i; } } - + // remove 'best' from the unvisited set FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best); @@ -579,59 +446,38 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, break; } - BOOST_FOREACH(FGTaxiSegment* seg, best->arcs()) { - if (!fullSearch && !seg->isPushBack()) { - continue; // inelligible! - } - - FGTaxiNode *tgt = seg->getEnd(); - double alt = best->getPathScore() + seg->getLength() + - seg->getPenalty(nParkings); - if (alt < tgt->getPathScore()) { // Relax (u,v) - tgt->setPathScore(alt); - tgt->setPreviousNode(best); - tgt->setPreviousSeg(seg); + BOOST_FOREACH(PositionedID targetId, cache->groundNetEdgesFrom(best->guid(), !fullSearch)) { + FGTaxiNode* tgt = (FGTaxiNode*) cache->loadById(targetId); + double edgeLength = dist(best->cart(), tgt->cart()); + double alt = searchData[best].score + edgeLength + edgePenalty(tgt); + if (alt < searchData[tgt].score) { // Relax (u,v) + searchData[tgt].score = alt; + searchData[tgt].previousNode = best; } } // of outgoing arcs/segments from current best node iteration } // of unvisited nodes remaining - if (lastNode->getPathScore() == HUGE_VAL) { + if (searchData[lastNode].score == HUGE_VAL) { // no valid route found if (fullSearch) { SG_LOG(SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " << parent->getId()); } - FGTaxiRoute empty; - return empty; - //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's - } else { - // assemble route from backtrace information - intVec nodes, routes; - FGTaxiNode *bt = lastNode; - while (bt->getPreviousNode() != 0) { - nodes.push_back(bt->getIndex()); - routes.push_back(bt->getPreviousSegment()->getIndex()); - bt = bt->getPreviousNode(); - } - nodes.push_back(start); - reverse(nodes.begin(), nodes.end()); - reverse(routes.begin(), routes.end()); - - return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0); - } -} - -int FGTaxiSegment::getPenalty(int nGates) -{ - int penalty = 0; - if (end->getIndex() < nGates) { - penalty += 10000; - } - if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active. - penalty += 1000; + + return FGTaxiRoute(); } - return penalty; + + // assemble route from backtrace information + PositionedIDVec nodes; + FGTaxiNode *bt = lastNode; + while (searchData[bt].previousNode != 0) { + nodes.push_back(bt->guid()); + bt = searchData[bt].previousNode; + } + nodes.push_back(start); + reverse(nodes.begin(), nodes.end()); + return FGTaxiRoute(nodes, searchData[lastNode].score, 0); } /* ATC Related Functions */ @@ -644,7 +490,8 @@ void FGGroundNetwork::announcePosition(int id, double radius, int leg, FGAIAircraft * aircraft) { - init(); + assert(parent); + TrafficVectorIterator i = activeTraffic.begin(); // Search search if the current id alread has an entry // This might be faster using a map instead of a vector, but let's start by taking a safe route @@ -1345,7 +1192,7 @@ void FGGroundNetwork::render(bool visible) } else { elevationStart = ((i)->getAircraft()->_getAltitude()); } - double elevationEnd = segments[pos]->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationEnd = segments[pos]->getEnd()->getElevationM(); //cerr << "Using elevation " << elevationEnd << endl; if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) { @@ -1406,8 +1253,8 @@ void FGGroundNetwork::render(bool visible) obj_trans->setDataVariance(osg::Object::STATIC); // Experimental: Calculate slope here, based on length, and the individual elevations - double elevationStart = segments[k]->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); - double elevationEnd = segments[k]->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationStart = segments[k]->getStart()->getElevationM(); + double elevationEnd = segments[k]->getEnd ()->getElevationM(); if ((elevationStart == 0) || (elevationStart == parent->getElevation())) { SGGeod center2 = segments[k]->getStart()->geod(); center2.setElevationM(SG_MAX_ELEVATION_M); diff --git a/src/Airports/groundnetwork.hxx b/src/Airports/groundnetwork.hxx index 9b73e2e81..de8d94ed4 100644 --- a/src/Airports/groundnetwork.hxx +++ b/src/Airports/groundnetwork.hxx @@ -72,57 +72,40 @@ typedef BlockList::iterator BlockListIterator; class FGTaxiSegment { private: - int startNode; - int endNode; - double length; - double heading; + const PositionedID startNode; + const PositionedID endNode; + bool isActive; - bool isPushBackRoute; BlockList blockTimes; - FGTaxiNode *start; - FGTaxiNode *end; + int index; FGTaxiSegment *oppositeDirection; + friend class FGGroundNetwork; public: - FGTaxiSegment(int start, int end, bool isPushBack); + FGTaxiSegment(PositionedID start, PositionedID end); void setIndex (int val) { index = val; }; - - void setOpposite(FGTaxiSegment *opp) { - oppositeDirection = opp; - }; - - bool bindToNodes(const IndexTaxiNodeMap& nodes); void setDimensions(double elevation); void block(int id, time_t blockTime, time_t now); void unblock(time_t now); bool hasBlock(time_t now); - FGTaxiNode * getEnd() { - return end; - }; - FGTaxiNode * getStart() { - return start; - }; - double getLength() { - return length; - }; - int getIndex() { - return index; - }; + FGTaxiNode * getEnd() const; + FGTaxiNode * getStart() const; + + double getLength() const; // compute the center of the arc SGGeod getCenter() const; - double getHeading() { - return heading; - }; - bool isPushBack() { - return isPushBackRoute; + double getHeading() const; + + int getIndex() { + return index; }; int getPenalty(int nGates); @@ -130,7 +113,7 @@ public: bool operator<(const FGTaxiSegment &other) const { return index < other.index; }; - //bool hasSmallerHeadingDiff (const FGTaxiSegment &other) const { return headingDiff < other.headingDiff; }; + FGTaxiSegment *opposite() { return oppositeDirection; }; @@ -150,60 +133,45 @@ typedef std::vector::iterator intVecIterator; class FGTaxiRoute { private: - intVec nodes; - intVec routes; + PositionedIDVec nodes; double distance; -// int depth; - intVecIterator currNode; - intVecIterator currRoute; + PositionedIDVec::iterator currNode; public: FGTaxiRoute() { distance = 0; currNode = nodes.begin(); - currRoute = routes.begin(); }; - FGTaxiRoute(intVec nds, intVec rts, double dist, int dpth) { + + FGTaxiRoute(const PositionedIDVec& nds, double dist, int dpth) { nodes = nds; - routes = rts; distance = dist; currNode = nodes.begin(); - currRoute = routes.begin(); -// depth = dpth; }; FGTaxiRoute& operator= (const FGTaxiRoute &other) { nodes = other.nodes; - routes = other.routes; distance = other.distance; -// depth = other.depth; currNode = nodes.begin(); - currRoute = routes.begin(); return *this; }; FGTaxiRoute(const FGTaxiRoute& copy) : nodes(copy.nodes), - routes(copy.routes), distance(copy.distance), -// depth(copy.depth), - currNode(nodes.begin()), - currRoute(routes.begin()) + currNode(nodes.begin()) {}; bool operator< (const FGTaxiRoute &other) const { return distance < other.distance; }; bool empty () { - return nodes.begin() == nodes.end(); + return nodes.empty(); }; - bool next(int *nde); - bool next(int *nde, int *rte); - void rewind(int legNr); - + bool next(PositionedID *nde); + void first() { currNode = nodes.begin(); - currRoute = routes.begin(); }; int size() { return nodes.size(); @@ -211,8 +179,6 @@ public: int nodesLeft() { return nodes.end() - currNode; }; - -// int getDepth() { return depth; }; }; typedef std::vector TaxiRouteVector; @@ -231,12 +197,8 @@ private: int count; int version; - IndexTaxiNodeMap nodes; - FGTaxiNodeVector pushBackNodes; - FGTaxiSegmentVector segments; - TaxiRouteVector routes; TrafficVector activeTraffic; TrafficVectorIterator currTraffic; @@ -255,18 +217,16 @@ private: void parseCache(); + + void loadSegments(); public: FGGroundNetwork(); ~FGGroundNetwork(); - - void addNode (FGTaxiNode* node); - void addNodes (FGParkingVec *parkings); - void addSegment(FGTaxiSegment* seg); - void setVersion (int v) { version = v;}; + void setVersion (int v) { version = v;}; int getVersion() { return version; }; - void init(); + void init(FGAirport* pr); bool exists() { return hasNetwork; }; @@ -274,21 +234,21 @@ public: towerController = twrCtrlr; }; - int findNearestNode(const SGGeod& aGeod); - int findNearestNodeOnRunway(const SGGeod& aGeod, FGRunway* aRunway = NULL); + int findNearestNode(const SGGeod& aGeod) const; + int findNearestNodeOnRunway(const SGGeod& aGeod, FGRunway* aRunway = NULL) const; - FGTaxiNode *findNode(unsigned idx); - FGTaxiSegment *findSegment(unsigned idx); - FGTaxiRoute findShortestRoute(int start, int end, bool fullSearch=true); - //void trace(FGTaxiNode *, int, int, double dist); - - int getNrOfNodes() { - return nodes.size(); - }; - - void setParent(FGAirport *par) { - parent = par; - }; + FGTaxiNode *findNode(PositionedID idx) const; + FGTaxiSegment *findSegment(unsigned idx) const; + + /** + * Find the taxiway segment joining two (ground-net) nodes. Returns + * NULL if no such segment exists. + * It is permitted to pass 0 for the 'to' ID, indicating that any + * segment originating at 'from' is acceptable. + */ + FGTaxiSegment* findSegment(PositionedID from, PositionedID to) const; + + FGTaxiRoute findShortestRoute(PositionedID start, PositionedID end, bool fullSearch=true); virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, diff --git a/src/Airports/parking.cxx b/src/Airports/parking.cxx index 58928dfcf..032a12549 100644 --- a/src/Airports/parking.cxx +++ b/src/Airports/parking.cxx @@ -31,24 +31,23 @@ #include #include "parking.hxx" -#include "groundnetwork.hxx" /********************************************************************************* * FGParking ********************************************************************************/ -FGParking::FGParking(PositionedID aGuid, int index, const SGGeod& pos, +FGParking::FGParking(PositionedID aGuid, const SGGeod& pos, double aHeading, double aRadius, const std::string& name, const std::string& aType, - const std::string& codes) : - FGTaxiNode(aGuid, index, pos, false, 0), + const std::string& codes, + PositionedID pushBackNode) : + FGTaxiNode(aGuid, pos, false, 0), heading(aHeading), radius(aRadius), parkingName(name), type(aType), airlineCodes(codes), - available(true), - pushBackPoint(0) + pushBackPoint(pushBackNode) { } diff --git a/src/Airports/parking.hxx b/src/Airports/parking.hxx index c16038c51..ff466d256 100644 --- a/src/Airports/parking.hxx +++ b/src/Airports/parking.hxx @@ -38,42 +38,33 @@ #include "gnnode.hxx" -class FGTaxiRoute; - class FGParking : public FGTaxiNode { private: - double heading; - double radius; - std::string parkingName; - std::string type; - std::string airlineCodes; - - bool available; - int pushBackPoint; - std::auto_ptr pushBackRoute; + const double heading; + const double radius; + const std::string parkingName; + const std::string type; + const std::string airlineCodes; + const PositionedID pushBackPoint; SG_DISABLE_COPY(FGParking); public: - FGParking(PositionedID aGuid, int index, const SGGeod& pos, + FGParking(PositionedID aGuid, const SGGeod& pos, double heading, double radius, const std::string& name, const std::string& type, - const std::string& codes); + const std::string& codes, + PositionedID pushBackNode); virtual ~FGParking(); - +#if 0 void setHeading (double hdg) { heading = hdg; }; void setRadius (double rad) { radius = rad; }; void setName (const std::string& name) { parkingName = name; }; void setType (const std::string& tpe) { type = tpe; }; void setCodes (const std::string& codes){ airlineCodes= codes;}; - - void setPushBackRoute(std::auto_ptr val) { pushBackRoute = val; }; - void setPushBackPoint(int val) { pushBackPoint = val; }; - - bool isAvailable () const { return available;}; - void setAvailable(bool val) { available = val; }; +#endif double getHeading () const { return heading; }; double getRadius () const { return radius; }; @@ -82,8 +73,6 @@ public: std::string getCodes () const { return airlineCodes;}; std::string getName () const { return parkingName; }; - FGTaxiRoute * getPushBackRoute () { return pushBackRoute.get(); }; - int getPushBackPoint () { return pushBackPoint; }; bool operator< (const FGParking &other) const { diff --git a/src/Airports/simple.cxx b/src/Airports/simple.cxx index aac730e50..84ed7ffab 100644 --- a/src/Airports/simple.cxx +++ b/src/Airports/simple.cxx @@ -112,7 +112,8 @@ FGAirportDynamics * FGAirport::getDynamics() _dynamics = new FGAirportDynamics(this); XMLLoader::load(_dynamics); - + _dynamics->init(); + FGRunwayPreference rwyPrefs(this); XMLLoader::load(&rwyPrefs); _dynamics->setRwyUse(rwyPrefs); diff --git a/src/Airports/xmlloader.cxx b/src/Airports/xmlloader.cxx index 8b35cabd5..901d1235f 100644 --- a/src/Airports/xmlloader.cxx +++ b/src/Airports/xmlloader.cxx @@ -20,6 +20,7 @@ #include #include #include +#include #include
#include
@@ -32,6 +33,8 @@ #include "simple.hxx" #include "runwayprefs.hxx" +#include + using std::string; XMLLoader::XMLLoader() {} @@ -39,10 +42,32 @@ XMLLoader::~XMLLoader() {} void XMLLoader::load(FGAirportDynamics* d) { - FGAirportDynamicsXMLLoader visitor(d); - if(loadAirportXMLDataIntoVisitor(d->parent()->ident(), "groundnet", visitor)) { - d->init(); + SGPath path; + if (!findAirportData(d->parent()->ident(), "groundnet", path)) { + return; + } + + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + if (!cache->isCachedFileModified(path)) { + return; } + + SG_LOG(SG_GENERAL, SG_INFO, "reading groundnet data from " << path); + SGTimeStamp t; + try { + cache->beginTransaction(); + t.stamp(); + { + FGAirportDynamicsXMLLoader visitor(d); + readXML(path.str(), visitor); + } // ensure visitor is destroyed so its destructor runs + cache->stampCacheFile(path); + cache->commitTransaction(); + } catch (sg_exception& e) { + cache->abortTransaction(); + } + + SG_LOG(SG_GENERAL, SG_INFO, "parsing XML took " << t.elapsedMSec()); } void XMLLoader::load(FGRunwayPreference* p) { diff --git a/src/Main/positioninit.cxx b/src/Main/positioninit.cxx index d4cdd1799..cb4663cc2 100644 --- a/src/Main/positioninit.cxx +++ b/src/Main/positioninit.cxx @@ -169,7 +169,7 @@ static bool fgSetPosFromAirportIDandParkpos( const string& id, const string& par return false; } - int gateID; + ParkingAssignment pka; double radius = fgGetDouble("/sim/dimensions/radius-m"); if ((parkpos == string("AVAILABLE")) && (radius > 0)) { string fltType; @@ -203,27 +203,25 @@ static bool fgSetPosFromAirportIDandParkpos( const string& id, const string& par } string acType; // Currently not used by findAvailable parking, so safe to leave empty. - gateID = dcs->getAvailableParking(radius, fltType, acType, acOperator); - if (gateID >=0 ) { + pka = dcs->getAvailableParking(radius, fltType, acType, acOperator); + if (pka.isValid()) { fgGetString("/sim/presets/parkpos"); - fgSetString("/sim/presets/parkpos", dcs->getParking(gateID)->getName()); + fgSetString("/sim/presets/parkpos", pka.parking()->getName()); } else { SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find a suitable parking at airport " << id ); return false; } } else { - gateID = dcs->findParkingByName(parkpos); - if (gateID < 0) { + pka = dcs->getParkingByName(parkpos); + if (!pka.isValid()) { SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find a parking at airport " << id << ":" << parkpos); return false; } } - FGParking* parking = dcs->getParking(gateID); - parking->setAvailable(false); - fgApplyStartOffset(parking->geod(), parking->getHeading()); + fgApplyStartOffset(pka.parking()->geod(), pka.parking()->getHeading()); return true; } diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 5d228c4bc..c5cd7feea 100644 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -60,6 +60,8 @@ #include "PositionedOctree.hxx" #include #include +#include +#include using std::string; @@ -68,7 +70,7 @@ using std::string; namespace { -const int SCHEMA_VERSION = 4; +const int SCHEMA_VERSION = 5; // bind a std::string to a sqlite statement. The std::string must live the // entire duration of the statement execution - do not pass a temporary @@ -461,6 +463,29 @@ public: ")"); runSQL("CREATE INDEX airway_edge_from ON airway_edge(a)"); + + runSQL("CREATE TABLE taxi_node (" + "hold_type INT," + "on_runway BOOL," + "pushback BOOL" + ")"); + + runSQL("CREATE TABLE parking (" + "heading FLOAT," + "radius INT," + "gate_type VARCHAR," + "airlines VARCHAR," + "pushback INT64" + ")"); + + runSQL("CREATE TABLE groundnet_edge (" + "airport INT64," + "a INT64," + "b INT64" + ")"); + + runSQL("CREATE INDEX groundnet_edge_airport ON groundnet_edge(airport)"); + runSQL("CREATE INDEX groundnet_edge_from ON groundnet_edge(a)"); } void prepareQueries() @@ -570,6 +595,7 @@ public: sqlite3_bind_int(findILS, 4, FGPositioned::ILS); sqlite3_bind_int(findILS, 5, FGPositioned::LOC); + // airways findAirway = prepare("SELECT rowid FROM airway WHERE network=?1 AND ident=?2"); insertAirway = prepare("INSERT INTO airway (ident, network) " "VALUES (?1, ?2)"); @@ -580,6 +606,47 @@ public: isPosInAirway = prepare("SELECT rowid FROM airway_edge WHERE network=?1 AND a=?2"); airwayEdgesFrom = prepare("SELECT airway, b FROM airway_edge WHERE network=?1 AND a=?2"); + + // parking / taxi-node graph + insertTaxiNode = prepare("INSERT INTO taxi_node (rowid, hold_type, on_runway, pushback) VALUES(?1, ?2, ?3, 0)"); + insertParkingPos = prepare("INSERT INTO parking (rowid, heading, radius, gate_type, airlines) " + "VALUES (?1, ?2, ?3, ?4, ?5)"); + setParkingPushBack = prepare("UPDATE parking SET pushback=?2 WHERE rowid=?1"); + + loadTaxiNodeStmt = prepare("SELECT hold_type, on_runway FROM taxi_node WHERE rowid=?1"); + loadParkingPos = prepare("SELECT heading, radius, gate_type, airlines, pushback FROM parking WHERE rowid=?1"); + taxiEdgesFrom = prepare("SELECT b FROM groundnet_edge WHERE a=?1"); + pushbackEdgesFrom = prepare("SELECT b FROM groundnet_edge, taxi_node WHERE " + "a=?1 AND groundnet_edge.b = taxi_node.rowid AND pushback=1"); + + insertTaxiEdge = prepare("INSERT INTO groundnet_edge (airport, a,b) VALUES(?1, ?2, ?3)"); + + markTaxiNodeAsPushback = prepare("UPDATE taxi_node SET pushback=1 WHERE rowid=?1"); + airportTaxiNodes = prepare("SELECT rowid FROM positioned WHERE (type=?2 OR type=?3) AND airport=?1"); + sqlite3_bind_int(airportTaxiNodes, 2, FGPositioned::PARKING); + sqlite3_bind_int(airportTaxiNodes, 3, FGPositioned::TAXI_NODE); + + airportPushbackNodes = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE "\ + "airport=?1 AND positioned.rowid=taxi_node.rowid AND pushback=1 " + "AND (type=?2 OR type=?3)"); + sqlite3_bind_int(airportPushbackNodes, 2, FGPositioned::PARKING); + sqlite3_bind_int(airportPushbackNodes, 3, FGPositioned::TAXI_NODE); + + findNearestTaxiNode = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE " + "positioned.rowid = taxi_node.rowid AND airport=?1 " + "ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?2, ?3, ?4) " + "LIMIT 1"); + + findNearestRunwayTaxiNode = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE " + "positioned.rowid = taxi_node.rowid AND airport=?1 " + "AND on_runway=1 " + "ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?2, ?3, ?4) "); + + findAirportParking = prepare("SELECT positioned.rowid FROM positioned, parking WHERE " + "airport=?1 AND type=?4 AND " + "radius >= ?2 AND gate_type = ?3 AND " + "parking.rowid=positioned.rowid"); + sqlite3_bind_int(findAirportParking, 4, FGPositioned::PARKING); } void writeIntProperty(const string& key, int value) @@ -678,6 +745,35 @@ public: return new FGNavRecord(rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway); } + FGPositioned* loadParking(sqlite3_int64 rowId, + const string& name, const SGGeod& pos, + PositionedID airport) + { + reset(loadParkingPos); + sqlite3_bind_int64(loadParkingPos, 1, rowId); + execSelect1(loadParkingPos); + + double heading = sqlite3_column_double(loadParkingPos, 0); + int radius = sqlite3_column_int(loadParkingPos, 1); + string aircraftType((char*) sqlite3_column_text(loadParkingPos, 2)); + string airlines((char*) sqlite3_column_text(loadParkingPos, 3)); + PositionedID pushBack = sqlite3_column_int64(loadParkingPos, 4); + + return new FGParking(rowId, pos, heading, radius, name, aircraftType, airlines, pushBack); + } + + FGPositioned* loadTaxiNode(sqlite3_int64 rowId, const SGGeod& pos, + PositionedID airport) + { + reset(loadTaxiNodeStmt); + sqlite3_bind_int64(loadTaxiNodeStmt, 1, rowId); + execSelect1(loadTaxiNodeStmt); + + int hold_type = sqlite3_column_int(loadTaxiNodeStmt, 0); + bool onRunway = sqlite3_column_int(loadTaxiNodeStmt, 1); + return new FGTaxiNode(rowId, pos, onRunway, hold_type); + } + PositionedID insertPositioned(FGPositioned::Type ty, const string& ident, const string& name, const SGGeod& pos, PositionedID apt, bool spatialIndex) @@ -822,6 +918,12 @@ public: sqlite3_stmt_ptr findAirway, insertAirwayEdge, isPosInAirway, airwayEdgesFrom, insertAirway; +// groundnet (parking, taxi node graph) + sqlite3_stmt_ptr loadTaxiNodeStmt, loadParkingPos, insertTaxiNode, insertParkingPos; + sqlite3_stmt_ptr taxiEdgesFrom, pushbackEdgesFrom, insertTaxiEdge, markTaxiNodeAsPushback, + airportTaxiNodes, airportPushbackNodes, findNearestTaxiNode, findAirportParking, + setParkingPushBack, findNearestRunwayTaxiNode; + // since there's many permutations of ident/name queries, we create // them programtically, but cache the exact query by its raw SQL once // used. @@ -910,6 +1012,12 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadFromStmt(sqlite3_stmt_ptr q case FGPositioned::FREQ_UNICOM: return loadComm(rowid, ty, ident, name, pos, aptId); + case FGPositioned::TAXI_NODE: + return loadTaxiNode(rowid, pos, aptId); + + case FGPositioned::PARKING: + return loadParking(rowid, ident, pos, aptId); + default: return NULL; } @@ -1173,6 +1281,20 @@ void NavDataCache::stampCacheFile(const SGPath& path) d->execInsert(d->stampFileCache); } +void NavDataCache::beginTransaction() +{ + d->runSQL("BEGIN"); +} + +void NavDataCache::commitTransaction() +{ + d->runSQL("COMMIT"); +} + +void NavDataCache::abortTransaction() +{ + d->runSQL("ROLLBACK"); +} FGPositioned* NavDataCache::loadById(PositionedID rowid) { @@ -1723,5 +1845,128 @@ PositionedID NavDataCache::findNavaidForRunway(PositionedID runway, FGPositioned return sqlite3_column_int64(d->findNavaidForRunway, 0); } +PositionedID +NavDataCache::insertParking(const std::string& name, const SGGeod& aPos, + PositionedID aAirport, + double aHeading, int aRadius, const std::string& aAircraftType, + const std::string& aAirlines) +{ + sqlite3_int64 rowId = d->insertPositioned(FGPositioned::PARKING, name, "", aPos, aAirport, false); + +// we need to insert a row into the taxi_node table, otherwise we can't maintain +// the appropriate pushback flag. + d->reset(d->insertTaxiNode); + sqlite3_bind_int64(d->insertTaxiNode, 1, rowId); + sqlite3_bind_int(d->insertTaxiNode, 2, 0); + sqlite3_bind_int(d->insertTaxiNode, 3, 0); + d->execInsert(d->insertTaxiNode); + + d->reset(d->insertParkingPos); + sqlite3_bind_int64(d->insertParkingPos, 1, rowId); + sqlite3_bind_double(d->insertParkingPos, 2, aHeading); + sqlite3_bind_int(d->insertParkingPos, 3, aRadius); + sqlite_bind_stdstring(d->insertParkingPos, 4, aAircraftType); + sqlite_bind_stdstring(d->insertParkingPos, 5, aAirlines); + return d->execInsert(d->insertParkingPos); +} + +void NavDataCache::setParkingPushBackRoute(PositionedID parking, PositionedID pushBackNode) +{ + d->reset(d->setParkingPushBack); + sqlite3_bind_int64(d->setParkingPushBack, 1, parking); + sqlite3_bind_int64(d->setParkingPushBack, 2, pushBackNode); + d->execUpdate(d->setParkingPushBack); +} + +PositionedID +NavDataCache::insertTaxiNode(const SGGeod& aPos, PositionedID aAirport, int aHoldType, bool aOnRunway) +{ + sqlite3_int64 rowId = d->insertPositioned(FGPositioned::TAXI_NODE, string(), string(), aPos, aAirport, false); + d->reset(d->insertTaxiNode); + sqlite3_bind_int64(d->insertTaxiNode, 1, rowId); + sqlite3_bind_int(d->insertTaxiNode, 2, aHoldType); + sqlite3_bind_int(d->insertTaxiNode, 3, aOnRunway); + return d->execInsert(d->insertTaxiNode); +} + +void NavDataCache::insertGroundnetEdge(PositionedID aAirport, PositionedID from, PositionedID to) +{ + d->reset(d->insertTaxiEdge); + sqlite3_bind_int64(d->insertTaxiEdge, 1, aAirport); + sqlite3_bind_int64(d->insertTaxiEdge, 2, from); + sqlite3_bind_int64(d->insertTaxiEdge, 3, to); + d->execInsert(d->insertTaxiEdge); +} + +PositionedIDVec NavDataCache::groundNetNodes(PositionedID aAirport, bool onlyPushback) +{ + sqlite3_stmt_ptr q = onlyPushback ? d->airportPushbackNodes : d->airportTaxiNodes; + d->reset(q); + sqlite3_bind_int64(q, 1, aAirport); + return d->selectIds(q); +} + +void NavDataCache::markGroundnetAsPushback(PositionedID nodeId) +{ + d->reset(d->markTaxiNodeAsPushback); + sqlite3_bind_int64(d->markTaxiNodeAsPushback, 1, nodeId); + d->execUpdate(d->markTaxiNodeAsPushback); +} + +static double headingDifferenceDeg(double crs1, double crs2) +{ + double diff = crs2 - crs1; + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); + return diff; +} + +PositionedID NavDataCache::findGroundNetNode(PositionedID airport, const SGGeod& aPos, + bool onRunway, FGRunway* aRunway) +{ + sqlite3_stmt_ptr q = onRunway ? d->findNearestRunwayTaxiNode : d->findNearestTaxiNode; + d->reset(q); + sqlite3_bind_int64(q, 1, airport); + + SGVec3d cartPos(SGVec3d::fromGeod(aPos)); + sqlite3_bind_double(q, 2, cartPos.x()); + sqlite3_bind_double(q, 3, cartPos.y()); + sqlite3_bind_double(q, 4, cartPos.z()); + + while (d->execSelect(q)) { + PositionedID id = sqlite3_column_int64(q, 0); + if (!aRunway) { + return id; + } + + // ensure found node lies on the runway + FGPositionedRef node = loadById(id); + double course = SGGeodesy::courseDeg(node->geod(), aRunway->end()); + if (fabs(headingDifferenceDeg(course, aRunway->headingDeg())) < 3.0 ) { + return id; + } + } + + return 0; +} + +PositionedIDVec NavDataCache::groundNetEdgesFrom(PositionedID pos, bool onlyPushback) +{ + sqlite3_stmt_ptr q = onlyPushback ? d->pushbackEdgesFrom : d->taxiEdgesFrom; + d->reset(q); + sqlite3_bind_int64(q, 1, pos); + return d->selectIds(q); +} + +PositionedIDVec NavDataCache::findAirportParking(PositionedID airport, const std::string& flightType, + int radius) +{ + d->reset(d->findAirportParking); + sqlite3_bind_int64(d->findAirportParking, 1, airport); + sqlite3_bind_int(d->findAirportParking, 2, radius); + sqlite_bind_stdstring(d->findAirportParking, 3, flightType); + + return d->selectIds(d->findAirportParking); +} + } // of namespace flightgear diff --git a/src/Navaids/NavDataCache.hxx b/src/Navaids/NavDataCache.hxx index 4080a608e..01cc20bdd 100644 --- a/src/Navaids/NavDataCache.hxx +++ b/src/Navaids/NavDataCache.hxx @@ -30,6 +30,7 @@ #include class SGPath; +class FGRunway; namespace flightgear { @@ -84,6 +85,16 @@ public: string_list readStringListProperty(const std::string& key); void writeStringListProperty(const std::string& key, const string_list& values); +// transaction API wrappers + void beginTransaction(); + void commitTransaction(); + void abortTransaction(); + + /** + * retrieve an FGPositioned from the cache. + * This may be trivial if the object is previously loaded, or require actual + * disk IO. + */ FGPositioned* loadById(PositionedID guid); PositionedID insertAirport(FGPositioned::Type ty, const std::string& ident, @@ -112,8 +123,25 @@ public: PositionedID createUserWaypoint(const std::string& ident, const SGGeod& aPos); + PositionedID insertParking(const std::string& name, const SGGeod& aPos, + PositionedID aAirport, + double aHeading, int aRadius, const std::string& aAircraftType, + const std::string& aAirlines); + + void setParkingPushBackRoute(PositionedID parking, PositionedID pushBackNode); + + PositionedID insertTaxiNode(const SGGeod& aPos, PositionedID aAirport, int aHoldType, bool aOnRunway); + + void insertGroundnetEdge(PositionedID aAirport, PositionedID from, PositionedID to); + + /// update the metar flag associated with an airport void setAirportMetar(const std::string& icao, bool hasMetar); + /** + * Modify the position of an existing item. + * Use with care, since loaded instances will not be updated (at present - + * this behaviour could in theorey be improved) + */ void updatePosition(PositionedID item, const SGGeod &pos); FGPositioned::List findAllWithIdent(const std::string& ident, @@ -125,15 +153,35 @@ public: const SGGeod& aPos, FGPositioned::Filter* aFilter); + /** + * Helper to implement the AirportSearch widget. Optimised text search of + * airport names and idents, returning a list suitable for passing directly + * to PLIB. + */ char** searchAirportNamesAndIdents(const std::string& aFilter); + /** + * Find the closest matching comm-station on a frequency, to a position. + * The filter with be used for both type ranging and to validate the result + * candidates. + */ FGPositionedRef findCommByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt); + /** + * find all items of a specified type (or range of types) at an airport + */ PositionedIDVec airportItemsOfType(PositionedID apt, FGPositioned::Type ty, FGPositioned::Type maxTy = FGPositioned::INVALID); + /** + * find the first match item of the specified type and ident, at an airport + */ PositionedID airportItemWithIdent(PositionedID apt, FGPositioned::Type ty, const std::string& ident); - + + /** + * Find all navaids matching a particular frequency, sorted by range from the + * supplied position. Type-range will be determined from the filter + */ PositionedIDVec findNavaidsByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt); /// overload version of the above that does not consider positioned when @@ -175,8 +223,15 @@ public: // airways int findAirway(int network, const std::string& aName); + /** + * insert an edge between two positioned nodes, into the network. + * The airway identifier will be set accordingly. No reverse edge is created + * by this method - edges are directional so a reverses must be explicitly + * created. + */ void insertEdge(int network, int airwayID, PositionedID from, PositionedID to); + /// is the specified positioned a node on the network? bool isInAirwayNetwork(int network, PositionedID pos); /** @@ -184,6 +239,17 @@ public: * in an airway */ AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos); + +// ground-network + PositionedIDVec groundNetNodes(PositionedID aAirport, bool onlyPushback); + void markGroundnetAsPushback(PositionedID nodeId); + + PositionedID findGroundNetNode(PositionedID airport, const SGGeod& aPos, + bool onRunway, FGRunway* aRunway = NULL); + PositionedIDVec groundNetEdgesFrom(PositionedID pos, bool onlyPushback); + + PositionedIDVec findAirportParking(PositionedID airport, const std::string& flightType, + int radius); private: NavDataCache(); diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index e76ab83f6..a9f2cd4bf 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -229,7 +229,7 @@ protected: void modifyPosition(const SGGeod& newPos); const PositionedID mGuid; - const SGGeod mPosition; + SGGeod mPosition; const SGVec3d mCart; const Type mType; const std::string mIdent; diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index 3c66a4d0f..24f5564ba 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -53,6 +53,7 @@ #include #include #include +#include using namespace flightgear; @@ -1339,13 +1340,16 @@ static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args) } FGAirportDynamics* dynamics = apt->getDynamics(); - for (int i=0; igetNrOfParkings(); ++i) { - FGParking* park = dynamics->getParking(i); - // filter out based on availability and type - if (onlyAvailable && !park->isAvailable()) { + PositionedIDVec parkings = flightgear::NavDataCache::instance()->airportItemsOfType(apt->guid(), + FGPositioned::PARKING); + + BOOST_FOREACH(PositionedID parking, parkings) { + // filter out based on availability and type + if (onlyAvailable && !dynamics->isParkingAvailable(parking)) { continue; } + FGParking* park = dynamics->getParking(parking); if (!type.empty() && (park->getType() != type)) { continue; } -- 2.39.5