1 // groundnet.cxx - Implimentation of the FlightGear airport ground handling code
3 // Written by Durk Talsma, started June 2005.
5 // Copyright (C) 2004 Durk Talsma.
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 //#include <plib/sg.h>
31 //#include <plib/ul.h>
33 //#include <Environment/environment_mgr.hxx>
34 //#include <Environment/environment.hxx>
35 //#include <simgear/misc/sg_path.hxx>
36 //#include <simgear/props/props.hxx>
37 //#include <simgear/structure/subsystem_mgr.hxx>
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/route/waypoint.hxx>
40 //#include <Main/globals.hxx>
41 //#include <Main/fg_props.hxx>
42 //#include <Airports/runways.hxx>
44 #include <AIModel/AIFlightPlan.hxx>
48 #include "groundnetwork.hxx"
50 /***************************************************************************
52 **************************************************************************/
54 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
56 FGTaxiNodeVectorIterator i = nodes->begin();
57 while (i != nodes->end())
59 //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
60 if ((*i)->getIndex() == startNode)
62 start = (*i)->getAddress();
63 (*i)->addSegment(this);
68 SG_LOG(SG_GENERAL, SG_ALERT, "Could not find start node " << startNode << endl);
71 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
73 FGTaxiNodeVectorIterator i = nodes->begin();
74 while (i != nodes->end())
76 //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
77 if ((*i)->getIndex() == endNode)
79 end = (*i)->getAddress();
84 SG_LOG(SG_GENERAL, SG_ALERT, "Could not find end node " << endNode << endl);
89 // There is probably a computationally cheaper way of
91 void FGTaxiSegment::setTrackDistance()
94 SGWayPoint first (start->getLongitude(),
97 SGWayPoint second (end->getLongitude(),
100 first.CourseAndDistance(second, &course, &length);
104 void FGTaxiSegment::setCourseDiff(double crse)
106 headingDiff = fabs(course-crse);
108 if (headingDiff > 180)
109 headingDiff = fabs(headingDiff - 360);
113 /***************************************************************************
115 **************************************************************************/
116 bool FGTaxiRoute::next(int *nde)
118 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
119 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
120 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
121 //if (currNode != nodes.end())
122 // cerr << "true" << endl;
124 // cerr << "false" << endl;
125 //if (nodes.size() != (routes.size()) +1)
126 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
128 if (currNode == nodes.end())
131 if (currNode != nodes.begin()) // make sure route corresponds to the end node
137 bool FGTaxiRoute::next(int *nde, int *rte)
139 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
140 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
141 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
142 //if (currNode != nodes.end())
143 // cerr << "true" << endl;
145 // cerr << "false" << endl;
146 if (nodes.size() != (routes.size()) +1) {
147 SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
150 if (currNode == nodes.end())
153 //*rte = *(currRoute);
154 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
161 // If currNode points to the first node, this means the aircraft is not on the taxi node
162 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
163 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
164 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
165 // unique for any starting location.
166 // Note that this is probably just a temporary fix until I get Parking / tower control working.
167 *rte = -1 * *(currRoute);
173 void FGTaxiRoute::rewind(int route)
179 if (!(next(&currPoint, &currRoute))) {
180 SG_LOG(SG_GENERAL,SG_ALERT, "Error in rewinding TaxiRoute: current" << currRoute
181 << " goal " << route);
183 } while (currRoute != route);
189 /***************************************************************************
191 **************************************************************************/
192 bool compare_nodes(FGTaxiNode *a, FGTaxiNode *b) {
196 bool compare_segments(FGTaxiSegment *a, FGTaxiSegment *b) {
200 FGGroundNetwork::FGGroundNetwork()
208 currTraffic = activeTraffic.begin();
212 FGGroundNetwork::~FGGroundNetwork()
214 for (FGTaxiNodeVectorIterator node = nodes.begin();
221 pushBackNodes.clear();
222 for (FGTaxiSegmentVectorIterator seg = segments.begin();
223 seg != segments.end();
231 void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
233 segments.push_back(new FGTaxiSegment(seg));
236 void FGGroundNetwork::addNode(const FGTaxiNode &node)
238 nodes.push_back(new FGTaxiNode(node));
241 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
244 FGParkingVecIterator i = parkings->begin();
245 while (i != parkings->end())
247 n.setIndex(i->getIndex());
248 n.setLatitude(i->getLatitude());
249 n.setLongitude(i->getLongitude());
250 nodes.push_back(new FGTaxiNode(n));
258 void FGGroundNetwork::init()
262 sort(nodes.begin(), nodes.end(), compare_nodes);
263 //sort(segments.begin(), segments.end(), compare_segments());
264 FGTaxiSegmentVectorIterator i = segments.begin();
265 while(i != segments.end()) {
266 (*i)->setStart(&nodes);
267 (*i)->setEnd (&nodes);
268 (*i)->setTrackDistance();
269 (*i)->setIndex(index);
270 if ((*i)->isPushBack()) {
271 pushBackNodes.push_back((*i)->getEnd());
273 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
274 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
275 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
276 // << (*i)->getEnd()->getIndex() << endl);
281 i = segments.begin();
282 while(i != segments.end()) {
283 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
284 while (j != (*i)->getEnd()->getEndRoute())
286 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex())
288 int start1 = (*i)->getStart()->getIndex();
289 int end1 = (*i)->getEnd() ->getIndex();
290 int start2 = (*j)->getStart()->getIndex();
291 int end2 = (*j)->getEnd()->getIndex();
292 int oppIndex = (*j)->getIndex();
293 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
294 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
295 (*i)->setOpposite(*j);
302 //FGTaxiNodeVectorIterator j = nodes.begin();
303 //while (j != nodes.end()) {
304 // if ((*j)->getHoldPointType() == 3) {
305 // pushBackNodes.push_back((*j));
309 //cerr << "Done initializing ground network" << endl;
315 int FGGroundNetwork::findNearestNode(double lat, double lon)
317 double minDist = HUGE_VAL;
320 SGWayPoint first (lon,
324 for (FGTaxiNodeVectorIterator
326 itr != nodes.end(); itr++)
329 SGWayPoint second ((*itr)->getLongitude(),
330 (*itr)->getLatitude(),
332 first.CourseAndDistance(second, &course, &dist);
336 index = (*itr)->getIndex();
337 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
343 FGTaxiNode *FGGroundNetwork::findNode(int idx)
345 for (FGTaxiNodeVectorIterator
347 itr != nodes.end(); itr++)
349 if (itr->getIndex() == idx)
350 return itr->getAddress();
353 if ((idx >= 0) && (idx < nodes.size()))
354 return nodes[idx]->getAddress();
359 FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
361 for (FGTaxiSegmentVectorIterator
362 itr = segments.begin();
363 itr != segments.end(); itr++)
365 if (itr->getIndex() == idx)
366 return itr->getAddress();
369 if ((idx > 0) && (idx <= segments.size()))
370 return segments[idx-1]->getAddress();
373 //cerr << "Alert: trying to find invalid segment " << idx << endl;
379 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, bool fullSearch)
381 //implements Dijkstra's algorithm to find shortest distance route from start to end
382 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
384 //double INFINITE = 100000000000.0;
385 // initialize scoring values
386 int nParkings = parent->getDynamics()->getNrOfParkings();
387 FGTaxiNodeVector *currNodesSet;
389 currNodesSet = &nodes;
391 currNodesSet = &pushBackNodes;
394 for (FGTaxiNodeVectorIterator
395 itr = currNodesSet->begin();
396 itr != currNodesSet->end(); itr++) {
397 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
398 (*itr)->setPreviousNode(0); //
399 (*itr)->setPreviousSeg (0); //
402 FGTaxiNode *firstNode = findNode(start);
403 firstNode->setPathScore(0);
405 FGTaxiNode *lastNode = findNode(end);
407 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
409 while (!unvisited.empty()) {
410 FGTaxiNode* best = *(unvisited.begin());
411 for (FGTaxiNodeVectorIterator
412 itr = unvisited.begin();
413 itr != unvisited.end(); itr++) {
414 if ((*itr)->getPathScore() < best->getPathScore())
418 FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best);
419 unvisited.erase(newend, unvisited.end());
421 if (best == lastNode) { // found route or best not connected
424 for (FGTaxiSegmentVectorIterator
425 seg = best->getBeginRoute();
426 seg != best->getEndRoute(); seg++) {
427 if (fullSearch || (*seg)->isPushBack()) {
428 FGTaxiNode* tgt = (*seg)->getEnd();
429 double alt = best->getPathScore() + (*seg)->getLength() + (*seg)->getPenalty(nParkings);
430 if (alt < tgt->getPathScore()) { // Relax (u,v)
431 tgt->setPathScore(alt);
432 tgt->setPreviousNode(best);
433 tgt->setPreviousSeg(*seg); //
436 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
442 if (lastNode->getPathScore() == HUGE_VAL) {
443 // no valid route found
445 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " <<
450 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
452 // assemble route from backtrace information
453 intVec nodes, routes;
454 FGTaxiNode* bt = lastNode;
455 while (bt->getPreviousNode() != 0) {
456 nodes.push_back(bt->getIndex());
457 routes.push_back(bt->getPreviousSegment()->getIndex());
458 bt = bt->getPreviousNode();
460 nodes.push_back(start);
461 reverse(nodes.begin(), nodes.end());
462 reverse(routes.begin(), routes.end());
464 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
468 int FGTaxiSegment::getPenalty(int nGates) {
470 if (end->getIndex() < nGates) {
473 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
479 /* ATC Related Functions */
481 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
482 double lat, double lon, double heading,
483 double speed, double alt, double radius, int leg,
484 FGAIAircraft *aircraft)
486 TrafficVectorIterator i = activeTraffic.begin();
487 // Search search if the current id alread has an entry
488 // This might be faster using a map instead of a vector, but let's start by taking a safe route
489 if (activeTraffic.size()) {
490 //while ((i->getId() != id) && i != activeTraffic.end()) {
491 while (i != activeTraffic.end()) {
492 if (i->getId() == id) {
498 // Add a new TrafficRecord if no one exsists for this aircraft.
499 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
502 rec.setPositionAndIntentions(currentPosition, intendedRoute);
503 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
504 rec.setRadius(radius); // only need to do this when creating the record.
505 rec.setAircraft(aircraft);
506 activeTraffic.push_back(rec);
508 i->setPositionAndIntentions(currentPosition, intendedRoute);
509 i->setPositionAndHeading(lat, lon, heading, speed, alt);
513 void FGGroundNetwork::signOff(int id) {
514 TrafficVectorIterator i = activeTraffic.begin();
515 // Search search if the current id alread has an entry
516 // This might be faster using a map instead of a vector, but let's start by taking a safe route
517 if (activeTraffic.size()) {
518 //while ((i->getId() != id) && i != activeTraffic.end()) {
519 while (i != activeTraffic.end()) {
520 if (i->getId() == id) {
526 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
527 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
529 i = activeTraffic.erase(i);
533 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt,
535 TrafficVectorIterator i = activeTraffic.begin();
536 // Search search if the current id has an entry
537 // This might be faster using a map instead of a vector, but let's start by taking a safe route
538 TrafficVectorIterator current, closest;
539 if (activeTraffic.size()) {
540 //while ((i->getId() != id) && i != activeTraffic.end()) {
541 while (i != activeTraffic.end()) {
542 if (i->getId() == id) {
548 // update position of the current aircraft
549 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
550 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
552 i->setPositionAndHeading(lat, lon, heading, speed, alt);
558 // Update every three secs, but add some randomness
559 // to prevent all IA objects doing this in synchrony
560 //if (getDt() < (3.0) + (rand() % 10))
564 current->clearResolveCircularWait();
565 current->setWaitsForId(0);
566 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
567 checkHoldPosition (id, lat, lon, heading, speed, alt);
568 if (checkForCircularWaits(id)) {
569 i->setResolveCircularWait();
574 Scan for a speed adjustment change. Find the nearest aircraft that is in front
575 and adjust speed when we get too close. Only do this when current position and/or
576 intentions of the current aircraft match current taxiroute position of the proximate
577 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
578 instruction. See below for the hold position instruction.
580 Note that there currently still is one flaw in the logic that needs to be addressed.
581 can be situations where one aircraft is in front of the current aircraft, on a separate
582 route, but really close after an intersection coming off the current route. This
583 aircraft is still close enough to block the current aircraft. This situation is currently
584 not addressed yet, but should be.
587 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
588 double lon, double heading,
589 double speed, double alt)
592 TrafficVectorIterator current, closest;
593 TrafficVectorIterator i = activeTraffic.begin();
594 bool otherReasonToSlowDown = false;
595 bool previousInstruction;
596 if (activeTraffic.size())
598 //while ((i->getId() != id) && (i != activeTraffic.end()))
599 while (i != activeTraffic.end()) {
600 if (i->getId() == id) {
610 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
611 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
616 previousInstruction = current->getSpeedAdjustment();
617 double mindist = HUGE_VAL;
618 if (activeTraffic.size())
620 double course, dist, bearing, minbearing;
621 SGWayPoint curr (lon,
624 //TrafficVector iterator closest;
626 for (TrafficVectorIterator i = activeTraffic.begin();
627 i != activeTraffic.end(); i++)
630 //SGWayPoint curr (lon,
633 SGWayPoint other (i->getLongitude (),
636 other.CourseAndDistance(curr, &course, &dist);
637 bearing = fabs(heading-course);
639 bearing = 360-bearing;
640 if ((dist < mindist) && (bearing < 60.0))
644 minbearing = bearing;
648 //Check traffic at the tower controller
649 if (towerController->hasActiveTraffic())
651 for (TrafficVectorIterator i = towerController->getActiveTraffic().begin();
652 i != towerController->getActiveTraffic().end(); i++)
654 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
655 //SGWayPoint curr (lon,
658 SGWayPoint other (i->getLongitude (),
661 other.CourseAndDistance(curr, &course, &dist);
662 bearing = fabs(heading-course);
664 bearing = 360-bearing;
665 if ((dist < mindist) && (bearing < 60.0))
669 minbearing = bearing;
670 otherReasonToSlowDown = true;
674 // Finally, check UserPosition
675 double userLatitude = fgGetDouble("/position/latitude-deg");
676 double userLongitude = fgGetDouble("/position/longitude-deg");
677 SGWayPoint user (userLongitude,
679 alt); // Alt is not really important here.
680 user.CourseAndDistance(curr, &course, &dist);
681 bearing = fabs(heading-course);
683 bearing = 360-bearing;
684 if ((dist < mindist) && (bearing < 60.0))
688 minbearing = bearing;
689 otherReasonToSlowDown = true;
692 // if (closest == current) {
693 // //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: closest and current match");
696 //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading
697 // << " course : " << course << endl;
698 current->clearSpeedAdjustment();
700 if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown)
702 double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
703 if (mindist < 2*maxAllowableDistance)
705 if (current->getId() == closest->getWaitsForId())
708 current->setWaitsForId(closest->getId());
709 if (closest->getId() != current->getId())
710 current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
712 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
713 if (mindist < maxAllowableDistance)
715 //double newSpeed = (maxAllowableDistance-mindist);
716 //current->setSpeedAdjustment(newSpeed);
717 //if (mindist < 0.5* maxAllowableDistance)
719 current->setSpeedAdjustment(0);
728 Check for "Hold position instruction".
729 The hold position should be issued under the following conditions:
730 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
731 2) For taxiing aircraft that use one taxiway in opposite directions
732 3) For crossing or merging taxiroutes.
735 void FGGroundNetwork::checkHoldPosition(int id, double lat,
736 double lon, double heading,
737 double speed, double alt)
740 TrafficVectorIterator current;
741 TrafficVectorIterator i = activeTraffic.begin();
742 if (activeTraffic.size())
744 //while ((i->getId() != id) && i != activeTraffic.end())
745 while (i != activeTraffic.end()) {
746 if (i->getId() == id) {
756 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
757 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
760 current->setHoldPosition(false);
761 SGWayPoint curr (lon,
764 double course, dist, bearing, minbearing;
765 for (i = activeTraffic.begin();
766 i != activeTraffic.end(); i++)
768 if (i->getId() != current->getId())
770 int node = current->crosses(this, *i);
773 // Determine whether it's save to continue or not.
774 // If we have a crossing route, there are two possibilities:
775 // 1) This is an interestion
776 // 2) This is oncoming two-way traffic, using the same taxiway.
777 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
778 SGWayPoint nodePos(findNode(node)->getLongitude (),
779 findNode(node)->getLatitude (),
782 SGWayPoint other (i->getLongitude (),
785 //other.CourseAndDistance(curr, &course, &dist);
788 if (current->isOpposing(this, *i, node))
792 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
793 // issue a "Hold Position" as soon as we're close to the offending node
794 // For now, I'm doing this as long as the other aircraft doesn't
795 // have a hold instruction as soon as we're within a reasonable
796 // distance from the offending node.
797 // This may be a bit of a conservative estimate though, as it may
798 // be well possible that both aircraft can both continue to taxi
799 // without crashing into each other.
804 other.CourseAndDistance(nodePos, &course, &dist);
805 if (dist > 200) // 2.0*i->getRadius())
808 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
814 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
817 curr.CourseAndDistance(nodePos, &course, &dist);
818 if (!(i->hasHoldPosition()))
821 if ((dist < 200) && //2.5*current->getRadius()) &&
823 (i->onRoute(this, *current)) &&
824 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
825 (!(current->getId() == i->getWaitsForId())))
826 //(!(i->getSpeedAdjustment()))) // &&
827 //(!(current->getSpeedAdjustment())))
830 current->setHoldPosition(true);
831 current->setWaitsForId(i->getId());
832 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
833 // << dist << " meters. Waiting for " << i->getCallSign();
835 //cerr <<" [opposing] " << endl;
837 // cerr << "[non-opposing] " << endl;
838 //if (i->hasSpeefAdjustment())
840 // cerr << " (which in turn waits for ) " << i->
844 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
853 * Check whether situations occur where the current aircraft is waiting for itself
854 * due to higher order interactions.
855 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
856 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
857 * through this list of waiting aircraft, we can check if we'd eventually end back
858 * at the current aircraft.
860 * Note that we should consider the situation where we are actually checking aircraft
861 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
862 * the looping aircraft. If we don't check for that, this function will get stuck into
866 bool FGGroundNetwork::checkForCircularWaits(int id)
868 //cerr << "Performing Wait check " << id << endl;
870 TrafficVectorIterator current, other;
871 TrafficVectorIterator i = activeTraffic.begin();
872 int trafficSize = activeTraffic.size();
874 while (i != activeTraffic.end()) {
875 if (i->getId() == id) {
884 if (i == activeTraffic.end() || (trafficSize == 0)) {
885 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
889 target = current->getWaitsForId();
890 //bool printed = false; // Note that this variable is for debugging purposes only.
894 //cerr << "aircraft waits for user" << endl;
899 while ((target > 0) && (target != id) && counter++ < trafficSize) {
901 TrafficVectorIterator i = activeTraffic.begin();
903 //while ((i->getId() != id) && i != activeTraffic.end())
904 while (i != activeTraffic.end()) {
905 if (i->getId() == target) {
914 if (i == activeTraffic.end() || (trafficSize == 0)) {
915 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
916 // The target id is not found on the current network, which means it's at the tower
917 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
921 target = other->getWaitsForId();
923 // actually this trap isn't as impossible as it first seemed:
924 // the setWaitsForID(id) is set to current when the aircraft
925 // is waiting for the user controlled aircraft.
926 //if (current->getId() == other->getId()) {
927 // cerr << "Caught the impossible trap" << endl;
928 // cerr << "Current = " << current->getId() << endl;
929 // cerr << "Other = " << other ->getId() << endl;
930 // for (TrafficVectorIterator at = activeTraffic.begin();
931 // at != activeTraffic.end();
933 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
936 if (current->getId() == other->getId())
939 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
940 // << " (" << other->getId() << "); " << endl;;
950 // cerr << "[done] " << endl << endl;;
952 SG_LOG(SG_GENERAL, SG_WARN, "Detected circular wait condition: Id = " << id << "target = " << target);
959 // Note that this function is probably obsolete...
960 bool FGGroundNetwork::hasInstruction(int id)
962 TrafficVectorIterator i = activeTraffic.begin();
963 // Search search if the current id has an entry
964 // This might be faster using a map instead of a vector, but let's start by taking a safe route
965 if (activeTraffic.size())
967 //while ((i->getId() != id) && i != activeTraffic.end()) {
968 while (i != activeTraffic.end()) {
969 if (i->getId() == id) {
975 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
976 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
978 return i->hasInstruction();
983 FGATCInstruction FGGroundNetwork::getInstruction(int id)
985 TrafficVectorIterator i = activeTraffic.begin();
986 // Search search if the current id has an entry
987 // This might be faster using a map instead of a vector, but let's start by taking a safe route
988 if (activeTraffic.size()) {
989 //while ((i->getId() != id) && i != activeTraffic.end()) {
990 while (i != activeTraffic.end()) {
991 if (i->getId() == id) {
997 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
998 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
1000 return i->getInstruction();
1002 return FGATCInstruction();