]> git.mxchange.org Git - flightgear.git/commitdiff
AI/ATC enhancements:
authorDurk Talsma <durk@localhost.(none)>
Sun, 7 Aug 2011 19:38:50 +0000 (21:38 +0200)
committerDurk Talsma <durk@localhost.(none)>
Sun, 7 Aug 2011 19:38:50 +0000 (21:38 +0200)
 - Better transistion between taxi and takeoff phases.
 - Skipping the pushback stages when a gate doesn't require push-back.
 - Some test code always chooses the user aircraft as the one that has to hold position.

src/AIModel/AIAircraft.cxx
src/AIModel/AIAircraft.hxx
src/AIModel/AIFlightPlan.cxx
src/AIModel/AIFlightPlan.hxx
src/AIModel/AIFlightPlanCreate.cxx
src/AIModel/AIFlightPlanCreatePushBack.cxx
src/ATC/atc_mgr.cxx
src/ATC/trafficcontrol.cxx
src/ATC/trafficcontrol.hxx
src/Airports/groundnetwork.cxx
src/Airports/groundnetwork.hxx

index eac6cd4fa59bb8460444a16a9f2b381d5ab87731..e5c37c8c8a864f63c783507018a291677ad37c80 100644 (file)
@@ -93,7 +93,7 @@ FGAIAircraft::FGAIAircraft(FGAISchedule *ref) :
 
     _performance = 0; //TODO initialize to JET_TRANSPORT from PerformanceDB
     dt = 0;
-    scheduledForTakeoff = false;
+    takeOffStatus = 0;
 }
 
 
@@ -557,8 +557,8 @@ void FGAIAircraft::announcePositionToController() {
     }
 }
 
-void FGAIAircraft::scheduleForATCTowerDepartureControl() {
-    if (!scheduledForTakeoff) {
+void FGAIAircraft::scheduleForATCTowerDepartureControl(int state) {
+    if (!takeOffStatus) {
         int leg = fp->getLeg();
         if (trafficRef) {
             if (trafficRef->getDepartureAirport()->getDynamics()) {
@@ -570,10 +570,11 @@ void FGAIAircraft::scheduleForATCTowerDepartureControl() {
                 towerController->announcePosition(getID(), fp, fp->getCurrentWaypoint()->getRouteIndex(),
                                                    _getLatitude(), _getLongitude(), hdg, speed, altitude_ft,
                                                     trafficRef->getRadius(), leg, this);
+                cerr << "Scheduling " << trafficRef->getCallSign() << " for takeoff " << endl;
             }
         }
     }
-    scheduledForTakeoff = true;
+    takeOffStatus = state;
 }
 
 // Process ATC instructions and report back
@@ -804,16 +805,18 @@ bool FGAIAircraft::handleAirportEndPoints(FGAIWaypoint* prev, time_t now) {
     // This waypoint marks the fact that the aircraft has passed the initial taxi
     // departure waypoint, so it can release the parking.
     //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());
         AccelTo(0.0);
-        setTaxiClearanceRequest(true);
+        //setTaxiClearanceRequest(true);
     }
     if (prev->contains("legend")) {
         fp->incrementLeg();
     }
     if (prev->contains(string("DepartureHold"))) {
-        scheduleForATCTowerDepartureControl();
+        cerr << "Passing point DepartureHold" << endl;
+        scheduleForATCTowerDepartureControl(2);
     }
 
     // This is the last taxi waypoint, and marks the the end of the flight plan
@@ -824,7 +827,7 @@ bool FGAIAircraft::handleAirportEndPoints(FGAIWaypoint* prev, time_t now) {
         if (nextDeparture < (now+1200)) {
             nextDeparture = now + 1200;
         }
-        fp->setTime(nextDeparture); // should be "next departure"
+        fp->setTime(nextDeparture);
     }
 
     return true;
index d8cbe9d604498e18dabf5f8b24a31878dedef7bc..7504aaecf504479329cbd9d49dfff78e5758b134 100644 (file)
@@ -77,9 +77,11 @@ public:
     bool getTaxiClearanceRequest() { return needsTaxiClearance; };
     FGAISchedule * getTrafficRef() { return trafficRef; };
     void setTrafficRef(FGAISchedule *ref) { trafficRef = ref; };
-    void scheduleForATCTowerDepartureControl();
+    void resetTakeOffStatus() { takeOffStatus = 0;};
+    void setTakeOffStatus(int status) { takeOffStatus = status; };
+    void scheduleForATCTowerDepartureControl(int state);
 
-    inline bool isScheduledForTakeoff() { return scheduledForTakeoff; };
+    //inline bool isScheduledForTakeoff() { return scheduledForTakeoff; };
 
     virtual const char* getTypeString(void) const { return "aircraft"; }
 
@@ -98,6 +100,8 @@ public:
     inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");};
     std::string atGate();
 
+    int getTakeOffStatus() { return takeOffStatus; };
+
     void checkTcas();
 
     FGATCController * getATCController() { return controller; };
@@ -173,7 +177,7 @@ private:
     bool reachedWaypoint;
     bool needsTaxiClearance;
     bool _needsGroundElevation;
-    bool scheduledForTakeoff;
+    int  takeOffStatus; // 1 = joined departure cue; 2 = Passed DepartureHold waypoint; handover control to tower; 0 = any other state. 
     time_t timeElapsed;
 
     PerformanceData* _performance; // the performance data for this aircraft
index c937716a40d30e52519bdd3f8d1f693cc55b1130..e87d11530ab3a4eb485359604b73827796e88c2a 100644 (file)
@@ -226,8 +226,10 @@ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac,
       
       if ((timeDiff > 60) && (timeDiff < 1200))
        leg = 2;
-      else if ((timeDiff >= 1200) && (timeDiff < 1500))
+      else if ((timeDiff >= 1200) && (timeDiff < 1500)) {
        leg = 3;
+        ac->setTakeOffStatus(2);
+      }
       else if ((timeDiff >= 1500) && (timeDiff < 2000))
        leg = 4;
       else if (timeDiff >= 2000)
index 95edce9d736bd59c67ab717a946c22567671f4e0..624ea16a82ef4fde9cb73a16adfc1dd0e8ff58eb 100644 (file)
@@ -1,4 +1,4 @@
-// // FGAIFlightPlan - class for loading and storing  AI flight plans
+// FGAIFlightPlan - class for loading and storing  AI flight plans
 // Written by David Culp, started May 2004
 // - davidculp2@comcast.net
 //
@@ -117,6 +117,7 @@ public:
 
    double getDistanceToGo(double lat, double lon, FGAIWaypoint* wp) const;
    int getLeg () const { return leg;};
+   
    void setLeadDistance(double speed, double bearing, FGAIWaypoint* current, FGAIWaypoint* next);
    void setLeadDistance(double distance_ft);
    double getLeadDistance( void ) const {return lead_distance;}
index ddd18078c6130af72f294ba7277ee11be2cb56c1..3ed42fb89d1ec91bff298a2c88095dab12182e62 100644 (file)
@@ -294,6 +294,7 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight,
 
     // push each node on the taxi route as a waypoint
     int route;
+    //cerr << "Building taxi route" << endl;
     while (taxiRoute->next(&node, &route)) {
         char buffer[10];
         snprintf(buffer, 10, "%d", node);
@@ -303,12 +304,20 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight,
             createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(),
                            ac->getPerformance()->vTaxi());
         wpt->setRouteIndex(route);
-        if (taxiRoute->size() == 1) {
+        //cerr << "Nodes left " << taxiRoute->nodesLeft() << " ";
+        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"));
         }
         waypoints.push_back(wpt);
     }
+    // Acceleration point, 105 meters into the runway,
+    SGGeod accelPoint = rwy->pointOnCenterline(105.0);
+    FGAIWaypoint *wpt = createOnGround(ac, "accel", accelPoint, apt->getElevation(), ac->getPerformance()->vRotate());
+    waypoints.push_back(wpt);
+
+    //cerr << "[done]" << endl;
     return true;
 }
 
@@ -452,17 +461,13 @@ bool FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight,
 
 
     double airportElev = apt->getElevation();
-    // Acceleration point, 105 meters into the runway,
-    SGGeod accelPoint = rwy->pointOnCenterline(105.0);
-    wpt = createOnGround(ac, "accel", accelPoint, airportElev, vRotate);
-    waypoints.push_back(wpt);
-
+    
 
     accelDistance =
         (vTakeoffMetric * vTakeoffMetric -
          vTaxiMetric * vTaxiMetric) / (2 * accelMetric);
     //cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl;
-    accelPoint = rwy->pointOnCenterline(105.0 + accelDistance);
+    SGGeod accelPoint = rwy->pointOnCenterline(105.0 + accelDistance);
     wpt = createOnGround(ac, "rotate", accelPoint, airportElev, vTakeoff);
     waypoints.push_back(wpt);
 
index 3a7e86d70fd75255dff38a8ebb543daedaf6554b..c5cdaca0d997aaed506e47f47a412476537ab63c 100644 (file)
@@ -139,7 +139,9 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac,
               // some special considerations for the last point:
               waypoints.back()->setName(string("PushBackPoint"));
               waypoints.back()->setSpeed(vTaxi);
+              ac->setTaxiClearanceRequest(true);
         } else {  // In case of a push forward departure...
+           ac->setTaxiClearanceRequest(false);
            double lat2 = 0.0, lon2 = 0.0, az2 = 0.0;
 
            //cerr << "Creating final push forward point for gate " << gateId << endl;
index 0c30d1f8990b91ee8a2ab13a1104d0f2818f58ab..41743e6e71a23632acd150a827fe15cfde00d0d3 100644 (file)
@@ -121,6 +121,7 @@ void FGATCManager::init() {
             string fltType = "ga";
             fp->setRunway(runway);
             fp->createTakeOff(&ai_ac, false, apt, 0, fltType);
+            ai_ac.setTakeOffStatus(2);
         } else {
             controller = apt->getDynamics()->getStartupController();
             int stationFreq = apt->getDynamics()->getGroundFrequency(1);
index 2690d5a3f7838c2eae61eb53f3bbb171b9269738..b80fcdefa052656a8b0f442eb9d09d32276beeca 100644 (file)
@@ -193,6 +193,11 @@ void FGTrafficRecord::setPositionAndIntentions(int pos,
     }
     //exit(1);
 }
+/**
+ * Check if another aircraft is ahead of the current one, and on the same 
+ * return true / false is the is/isn't the case.
+ *
+ ****************************************************************************/
 
 bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
 {
@@ -509,9 +514,15 @@ void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
     string transponderCode;
     FGAIFlightPlan *fp;
     string fltRules;
+    string instructionText;
 
     //double commFreqD;
     sender = rec->getAircraft()->getTrafficRef()->getCallSign();
+    if (rec->getAircraft()->getTaxiClearanceRequest()) {
+        instructionText = "push-back and taxi";
+    } else {
+        instructionText = "taxi";
+    }
     //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
     switch (rec->getLeg()) {
     case 1:
@@ -588,7 +599,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
             receiver + ". Start-up approved. " + atisInformation +
             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
             transponderCode + ". " +
-            "For push-back and taxi clearance call " + taxiFreqStr + ". " +
+            "For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
             sender + " control.";
         break;
     case MSG_DENY_ENGINE_START:
@@ -606,11 +617,12 @@ void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
         taxiFreqStr = formatATCFrequency3_2(taxiFreq);
         activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
         transponderCode = rec->getAircraft()->GetTransponderCode();
+
         text =
             receiver + ". Start-up approved. " + atisInformation +
             " correct, runway " + activeRunway + ", " + SID + ", squawk " +
             transponderCode + ". " +
-            "For push-back and taxi clearance call " + taxiFreqStr + ". " +
+            "For " + instructionText + " clearance call " + taxiFreqStr + ". " +
             sender;
         break;
     case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
@@ -624,10 +636,18 @@ void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId,
         text = receiver + ". Roger. " + sender;
         break;
     case MSG_REQUEST_PUSHBACK_CLEARANCE:
-        text = receiver + ". Request push-back. " + sender;
+        if (rec->getAircraft()->getTaxiClearanceRequest()) {
+            text = receiver + ". Request push-back. " + sender;
+        } else {
+            text = receiver + ". Request Taxi clearance. " + sender;
+        }
         break;
     case MSG_PERMIT_PUSHBACK_CLEARANCE:
-        text = receiver + ". Push-back approved. " + sender;
+        if (rec->getAircraft()->getTaxiClearanceRequest()) {
+            text = receiver + ". Push-back approved. " + sender;
+        } else {
+            text = receiver + ". Cleared to Taxi." + sender;
+        }
         break;
     case MSG_HOLD_PUSHBACK_CLEARANCE:
         text = receiver + ". Standby. " + sender;
@@ -753,9 +773,29 @@ void FGTowerController::announcePosition(int id,
         rec.setRunway(intendedRoute->getRunway());
         rec.setLeg(leg);
         //rec.setCallSign(callsign);
+        rec.setRadius(radius);
         rec.setAircraft(ref);
         activeTraffic.push_back(rec);
-        cerr << ref->getTrafficRef()->getCallSign() << " You are number " << activeTraffic.size() <<  " for takeoff " << endl;
+        // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway. 
+        ActiveRunwayVecIterator rwy = activeRunways.begin();
+        if (activeRunways.size()) {
+            while (rwy != activeRunways.end()) {
+                if (rwy->getRunwayName() == intendedRoute->getRunway()) {
+                    break;
+                }
+                rwy++;
+            }
+        }
+        if (rwy == activeRunways.end()) {
+            ActiveRunway aRwy(intendedRoute->getRunway(), id);
+            aRwy.addToDepartureCue(ref);
+            activeRunways.push_back(aRwy);
+            rwy = (activeRunways.end()-1);
+        } else {
+            rwy->addToDepartureCue(ref);
+        }
+
+        cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() <<  " for takeoff " << endl;
     } else {
         i->setPositionAndHeading(lat, lon, heading, speed, alt);
     }
@@ -788,29 +828,43 @@ void FGTowerController::updateAircraftInformation(int id, double lat, double lon
     }
     setDt(getDt() + dt);
 
-//    // see if we already have a clearance record for the currently active runway
+    // see if we already have a clearance record for the currently active runway
+    // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
+    // already exists here. So, we can simplify the current code. 
     ActiveRunwayVecIterator rwy = activeRunways.begin();
-    // again, a map might be more efficient here
-    if (activeRunways.size()) {
-        //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) {
-        while (rwy != activeRunways.end()) {
-            if (rwy->getRunwayName() == current->getRunway()) {
-                break;
-            }
-            rwy++;
+    while (rwy != activeRunways.end()) {
+        if (rwy->getRunwayName() == current->getRunway()) {
+            break;
         }
+        rwy++;
     }
-    if (rwy == activeRunways.end()) {
-        ActiveRunway aRwy(current->getRunway(), id);
-        activeRunways.push_back(aRwy);  // Since there are no clearance records for this runway yet
-        current->setHoldPosition(false);        // Clear the current aircraft to continue
-    } else {
-        // Okay, we have a clearance record for this runway, so check
-        // whether the clearence ID matches that of the current aircraft
-        if (id == rwy->getCleared()) {
-            current->setHoldPosition(false);
+
+    // only bother running the following code if the current aircraft is the
+    // first in line for depature
+    /* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
+        if (rwy->getCleared()) {
+            if (id == rwy->getCleared()) {
+                current->setHoldPosition(false);
+            } else {
+                current->setHoldPosition(true);
+            }
         } else {
-            current->setHoldPosition(true);
+            // For now. At later stages, this will probably be the place to check for inbound traffc.
+            rwy->setCleared(id);
+        }
+    } */
+    // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
+    if (current->getAircraft()->getTakeOffStatus() == 2) {
+        current->setHoldPosition(true);
+        int clearanceId = rwy->getCleared();
+        if (clearanceId) {
+            if (id == clearanceId) {
+                current->setHoldPosition(false);
+            }
+        } else {
+            if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
+                rwy->setCleared(id);
+            }
         }
     }
 }
@@ -841,7 +895,8 @@ void FGTowerController::signOff(int id)
             rwy++;
         }
         if (rwy != activeRunways.end()) {
-            rwy = activeRunways.erase(rwy);
+            rwy->setCleared(0);
+            rwy->updateDepartureCue();
         } else {
             SG_LOG(SG_GENERAL, SG_ALERT,
                    "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff");
@@ -851,6 +906,7 @@ void FGTowerController::signOff(int id)
         SG_LOG(SG_GENERAL, SG_ALERT,
                "AI error: Aircraft without traffic record is signing off from tower");
     } else {
+        i->getAircraft()->resetTakeOffStatus();
         i = activeTraffic.erase(i);
         //cerr << "Signing off from tower controller" << endl;
     }
index fcbaf9bf07a2a029a1bdd3c47b25991294074ab1..98e85d2205477da3c7404579a4b46a0c5ea52539 100644 (file)
@@ -197,6 +197,8 @@ typedef vector<FGTrafficRecord>::iterator TrafficVectorIterator;
 typedef vector<time_t> TimeVector;
 typedef vector<time_t>::iterator TimeVectorIterator;
 
+typedef vector<FGAIAircraft*> AircraftVec;
+typedef vector<FGAIAircraft*>::iterator AircraftVecIterator;
 
 /***********************************************************************
  * Active runway, a utility class to keep track of which aircraft has
@@ -209,6 +211,8 @@ private:
   int currentlyCleared;
   double distanceToFinal;
   TimeVector estimatedArrivalTimes;
+  AircraftVec departureCue;
+
 public:
   ActiveRunway(string r, int cc) { rwy = r; currentlyCleared = cc; distanceToFinal = 6.0 * SG_NM_TO_METER; };
   
@@ -218,7 +222,13 @@ public:
   //time_t getEstApproachTime() { return estimatedArrival; };
 
   //void setEstApproachTime(time_t time) { estimatedArrival = time; };
+  void addToDepartureCue(FGAIAircraft *ac) { departureCue.push_back(ac); };
+  void setCleared(int number) { currentlyCleared = number; };
   time_t requestTimeSlot(time_t eta);
+
+   int getDepartureCueSize() { return departureCue.size(); };
+   FGAIAircraft* getFirstAircraftInDepartureCue() { return departureCue.size() ? *(departureCue.begin()) : NULL; };
+   void updateDepartureCue() { departureCue.erase(departureCue.begin()); }
 };
 
 typedef vector<ActiveRunway> ActiveRunwayVec;
index 32dbdfc15ef55bc3a9d342007b9d15ea5be4f865..0d13e80ef5b9031a33326728ba75803427a4199f 100644 (file)
@@ -173,6 +173,7 @@ bool FGTaxiRoute::next(int *nde, int *rte)
     return true;
 };
 
+
 void FGTaxiRoute::rewind(int route)
 {
     int currPoint;
@@ -534,7 +535,15 @@ void FGGroundNetwork::signOff(int id)
         i = activeTraffic.erase(i);
     }
 }
-
+/**
+ * The ground network can deal with the following states:
+ * 0 =  Normal; no action required
+ * 1 = "Acknowledge "Hold position
+ * 2 = "Acknowledge "Resume taxi".
+ * 3 = "Issue TaxiClearance"
+ * 4 = =Acknowledge Taxi Clearance"
+ *
+ *************************************************************************************************************************/
 bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId,
                                AtcMsgDir msgDir)
 {
@@ -634,7 +643,7 @@ void FGGroundNetwork::updateAircraftInformation(int id, double lat, double lon,
         if ((state == 5) && available) {
             current->setState(0);
             current->getAircraft()->setTaxiClearanceRequest(false);
-            current->setHoldPosition(true);
+            current->setHoldPosition(false);
             available = false;
         }
 
@@ -723,6 +732,10 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
                 if (bearing > 180)
                     bearing = 360 - bearing;
                 if ((dist < mindist) && (bearing < 60.0)) {
+                    //cerr << "Current aircraft " << current->getAircraft()->getTrafficRef()->getCallSign()
+                    //     << " is closest to " << i->getAircraft()->getTrafficRef()->getCallSign() 
+                    //     << ", which has status " << i->getAircraft()->isScheduledForTakeoff() 
+                    //     << endl;
                     mindist = dist;
                     closest = i;
                     minbearing = bearing;
@@ -760,13 +773,18 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
                     return;
                 else
                     current->setWaitsForId(closest->getId());
-                if (closest->getId() != current->getId())
+                if (closest->getId() != current->getId()) {
                     current->setSpeedAdjustment(closest->getSpeed() *
                                                 (mindist / 100));
-                    if (closest->getAircraft()->isScheduledForTakeoff())
-                        current->getAircraft()->scheduleForATCTowerDepartureControl();
-                else
+                    if ( 
+                        closest->getAircraft()->getTakeOffStatus() && 
+                        (current->getAircraft()->getTrafficRef()->getDepartureAirport() ==  closest->getAircraft()->getTrafficRef()->getDepartureAirport()) &&
+                        (current->getAircraft()->GetFlightPlan()->getRunway() == closest->getAircraft()->GetFlightPlan()->getRunway())
+                       )
+                            current->getAircraft()->scheduleForATCTowerDepartureControl(1); 
+                } else {
                     current->setSpeedAdjustment(0);     // This can only happen when the user aircraft is the one closest
+                }
                 if (mindist < maxAllowableDistance) {
                     //double newSpeed = (maxAllowableDistance-mindist);
                     //current->setSpeedAdjustment(newSpeed);
@@ -867,8 +885,10 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat,
                         //(!(current->getSpeedAdjustment())))
 
                     {
-                        current->setHoldPosition(true);
-                        current->setWaitsForId(i->getId());
+                        if (!(isUserAircraft(i->getAircraft()))) { // test code. Don't wait for the user, let the user wait for you.
+                            current->setHoldPosition(true);
+                            current->setWaitsForId(i->getId());
+                        }
                         //cerr << "Hold check 5: " << current->getCallSign() <<"  Setting Hold Position: distance to node ("  << node << ") "
                         //           << dist << " meters. Waiting for " << i->getCallSign();
                         //if (opposing)
index 87dafe0a01d44f65dc9517aa2bf754b1b529156c..3de47c2bafb2b1cfc4f7929ba7d616356a315a16 100644 (file)
@@ -212,6 +212,8 @@ public:
   
   void first() { currNode = nodes.begin(); currRoute = routes.begin(); };
   int size() { return nodes.size(); };
+  int nodesLeft() { return nodes.end() - currNode; };
+
 //  int getDepth() { return depth; };
 };