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 <simgear/debug/logstream.hxx>
31 #include <simgear/route/waypoint.hxx>
33 #include <Airports/dynamics.hxx>
35 #include <AIModel/AIFlightPlan.hxx>
37 #include "groundnetwork.hxx"
39 /***************************************************************************
41 **************************************************************************/
43 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
45 FGTaxiNodeVectorIterator i = nodes->begin();
46 while (i != nodes->end())
48 //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
49 if ((*i)->getIndex() == startNode)
51 start = (*i)->getAddress();
52 (*i)->addSegment(this);
57 SG_LOG(SG_GENERAL, SG_ALERT, "Could not find start node " << startNode << endl);
60 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
62 FGTaxiNodeVectorIterator i = nodes->begin();
63 while (i != nodes->end())
65 //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
66 if ((*i)->getIndex() == endNode)
68 end = (*i)->getAddress();
73 SG_LOG(SG_GENERAL, SG_ALERT, "Could not find end node " << endNode << endl);
78 // There is probably a computationally cheaper way of
80 void FGTaxiSegment::setTrackDistance()
83 SGWayPoint first (start->getLongitude(),
86 SGWayPoint second (end->getLongitude(),
89 first.CourseAndDistance(second, &course, &length);
93 void FGTaxiSegment::setCourseDiff(double crse)
95 headingDiff = fabs(course-crse);
97 if (headingDiff > 180)
98 headingDiff = fabs(headingDiff - 360);
102 /***************************************************************************
104 **************************************************************************/
105 bool FGTaxiRoute::next(int *nde)
107 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
108 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
109 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
110 //if (currNode != nodes.end())
111 // cerr << "true" << endl;
113 // cerr << "false" << endl;
114 //if (nodes.size() != (routes.size()) +1)
115 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
117 if (currNode == nodes.end())
120 if (currNode != nodes.begin()) // make sure route corresponds to the end node
126 bool FGTaxiRoute::next(int *nde, int *rte)
128 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
129 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
130 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
131 //if (currNode != nodes.end())
132 // cerr << "true" << endl;
134 // cerr << "false" << endl;
135 if (nodes.size() != (routes.size()) +1) {
136 SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
139 if (currNode == nodes.end())
142 //*rte = *(currRoute);
143 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
150 // If currNode points to the first node, this means the aircraft is not on the taxi node
151 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
152 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
153 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
154 // unique for any starting location.
155 // Note that this is probably just a temporary fix until I get Parking / tower control working.
156 *rte = -1 * *(currRoute);
162 void FGTaxiRoute::rewind(int route)
168 if (!(next(&currPoint, &currRoute))) {
169 SG_LOG(SG_GENERAL,SG_ALERT, "Error in rewinding TaxiRoute: current" << currRoute
170 << " goal " << route);
172 } while (currRoute != route);
178 /***************************************************************************
180 **************************************************************************/
181 bool compare_nodes(FGTaxiNode *a, FGTaxiNode *b) {
185 bool compare_segments(FGTaxiSegment *a, FGTaxiSegment *b) {
189 FGGroundNetwork::FGGroundNetwork()
197 currTraffic = activeTraffic.begin();
201 FGGroundNetwork::~FGGroundNetwork()
203 for (FGTaxiNodeVectorIterator node = nodes.begin();
210 pushBackNodes.clear();
211 for (FGTaxiSegmentVectorIterator seg = segments.begin();
212 seg != segments.end();
220 void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
222 segments.push_back(new FGTaxiSegment(seg));
225 void FGGroundNetwork::addNode(const FGTaxiNode &node)
227 nodes.push_back(new FGTaxiNode(node));
230 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
233 FGParkingVecIterator i = parkings->begin();
234 while (i != parkings->end())
236 n.setIndex(i->getIndex());
237 n.setLatitude(i->getLatitude());
238 n.setLongitude(i->getLongitude());
239 nodes.push_back(new FGTaxiNode(n));
247 void FGGroundNetwork::init()
251 sort(nodes.begin(), nodes.end(), compare_nodes);
252 //sort(segments.begin(), segments.end(), compare_segments());
253 FGTaxiSegmentVectorIterator i = segments.begin();
254 while(i != segments.end()) {
255 (*i)->setStart(&nodes);
256 (*i)->setEnd (&nodes);
257 (*i)->setTrackDistance();
258 (*i)->setIndex(index);
259 if ((*i)->isPushBack()) {
260 pushBackNodes.push_back((*i)->getEnd());
262 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
263 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
264 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
265 // << (*i)->getEnd()->getIndex() << endl);
270 i = segments.begin();
271 while(i != segments.end()) {
272 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
273 while (j != (*i)->getEnd()->getEndRoute())
275 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex())
277 int start1 = (*i)->getStart()->getIndex();
278 int end1 = (*i)->getEnd() ->getIndex();
279 int start2 = (*j)->getStart()->getIndex();
280 int end2 = (*j)->getEnd()->getIndex();
281 int oppIndex = (*j)->getIndex();
282 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
283 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
284 (*i)->setOpposite(*j);
291 //FGTaxiNodeVectorIterator j = nodes.begin();
292 //while (j != nodes.end()) {
293 // if ((*j)->getHoldPointType() == 3) {
294 // pushBackNodes.push_back((*j));
298 //cerr << "Done initializing ground network" << endl;
302 int FGGroundNetwork::findNearestNode(const SGGeod& aGeod)
304 return findNearestNode(aGeod.getLatitudeDeg(), aGeod.getLongitudeDeg());
307 int FGGroundNetwork::findNearestNode(double lat, double lon)
309 double minDist = HUGE_VAL;
312 SGWayPoint first (lon,
316 for (FGTaxiNodeVectorIterator
318 itr != nodes.end(); itr++)
321 SGWayPoint second ((*itr)->getLongitude(),
322 (*itr)->getLatitude(),
324 first.CourseAndDistance(second, &course, &dist);
328 index = (*itr)->getIndex();
329 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
335 FGTaxiNode *FGGroundNetwork::findNode(int idx)
337 for (FGTaxiNodeVectorIterator
339 itr != nodes.end(); itr++)
341 if (itr->getIndex() == idx)
342 return itr->getAddress();
345 if ((idx >= 0) && (idx < nodes.size()))
346 return nodes[idx]->getAddress();
351 FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
353 for (FGTaxiSegmentVectorIterator
354 itr = segments.begin();
355 itr != segments.end(); itr++)
357 if (itr->getIndex() == idx)
358 return itr->getAddress();
361 if ((idx > 0) && (idx <= segments.size()))
362 return segments[idx-1]->getAddress();
365 //cerr << "Alert: trying to find invalid segment " << idx << endl;
371 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, bool fullSearch)
373 //implements Dijkstra's algorithm to find shortest distance route from start to end
374 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
376 //double INFINITE = 100000000000.0;
377 // initialize scoring values
378 int nParkings = parent->getDynamics()->getNrOfParkings();
379 FGTaxiNodeVector *currNodesSet;
381 currNodesSet = &nodes;
383 currNodesSet = &pushBackNodes;
386 for (FGTaxiNodeVectorIterator
387 itr = currNodesSet->begin();
388 itr != currNodesSet->end(); itr++) {
389 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
390 (*itr)->setPreviousNode(0); //
391 (*itr)->setPreviousSeg (0); //
394 FGTaxiNode *firstNode = findNode(start);
395 firstNode->setPathScore(0);
397 FGTaxiNode *lastNode = findNode(end);
399 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
401 while (!unvisited.empty()) {
402 FGTaxiNode* best = *(unvisited.begin());
403 for (FGTaxiNodeVectorIterator
404 itr = unvisited.begin();
405 itr != unvisited.end(); itr++) {
406 if ((*itr)->getPathScore() < best->getPathScore())
410 FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best);
411 unvisited.erase(newend, unvisited.end());
413 if (best == lastNode) { // found route or best not connected
416 for (FGTaxiSegmentVectorIterator
417 seg = best->getBeginRoute();
418 seg != best->getEndRoute(); seg++) {
419 if (fullSearch || (*seg)->isPushBack()) {
420 FGTaxiNode* tgt = (*seg)->getEnd();
421 double alt = best->getPathScore() + (*seg)->getLength() + (*seg)->getPenalty(nParkings);
422 if (alt < tgt->getPathScore()) { // Relax (u,v)
423 tgt->setPathScore(alt);
424 tgt->setPreviousNode(best);
425 tgt->setPreviousSeg(*seg); //
428 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
434 if (lastNode->getPathScore() == HUGE_VAL) {
435 // no valid route found
437 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " <<
442 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
444 // assemble route from backtrace information
445 intVec nodes, routes;
446 FGTaxiNode* bt = lastNode;
447 while (bt->getPreviousNode() != 0) {
448 nodes.push_back(bt->getIndex());
449 routes.push_back(bt->getPreviousSegment()->getIndex());
450 bt = bt->getPreviousNode();
452 nodes.push_back(start);
453 reverse(nodes.begin(), nodes.end());
454 reverse(routes.begin(), routes.end());
456 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
460 int FGTaxiSegment::getPenalty(int nGates) {
462 if (end->getIndex() < nGates) {
465 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
471 /* ATC Related Functions */
473 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
474 double lat, double lon, double heading,
475 double speed, double alt, double radius, int leg,
476 FGAIAircraft *aircraft)
478 TrafficVectorIterator i = activeTraffic.begin();
479 // Search search if the current id alread has an entry
480 // This might be faster using a map instead of a vector, but let's start by taking a safe route
481 if (activeTraffic.size()) {
482 //while ((i->getId() != id) && i != activeTraffic.end()) {
483 while (i != activeTraffic.end()) {
484 if (i->getId() == id) {
490 // Add a new TrafficRecord if no one exsists for this aircraft.
491 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
494 rec.setPositionAndIntentions(currentPosition, intendedRoute);
495 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
496 rec.setRadius(radius); // only need to do this when creating the record.
497 rec.setAircraft(aircraft);
498 activeTraffic.push_back(rec);
500 i->setPositionAndIntentions(currentPosition, intendedRoute);
501 i->setPositionAndHeading(lat, lon, heading, speed, alt);
505 void FGGroundNetwork::signOff(int id) {
506 TrafficVectorIterator i = activeTraffic.begin();
507 // Search search if the current id alread has an entry
508 // This might be faster using a map instead of a vector, but let's start by taking a safe route
509 if (activeTraffic.size()) {
510 //while ((i->getId() != id) && i != activeTraffic.end()) {
511 while (i != activeTraffic.end()) {
512 if (i->getId() == id) {
518 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
519 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
521 i = activeTraffic.erase(i);
525 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt,
527 TrafficVectorIterator i = activeTraffic.begin();
528 // Search search if the current id has an entry
529 // This might be faster using a map instead of a vector, but let's start by taking a safe route
530 TrafficVectorIterator current, closest;
531 if (activeTraffic.size()) {
532 //while ((i->getId() != id) && i != activeTraffic.end()) {
533 while (i != activeTraffic.end()) {
534 if (i->getId() == id) {
540 // update position of the current aircraft
541 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
542 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
544 i->setPositionAndHeading(lat, lon, heading, speed, alt);
550 // Update every three secs, but add some randomness
551 // to prevent all IA objects doing this in synchrony
552 //if (getDt() < (3.0) + (rand() % 10))
556 current->clearResolveCircularWait();
557 current->setWaitsForId(0);
558 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
559 checkHoldPosition (id, lat, lon, heading, speed, alt);
560 if (checkForCircularWaits(id)) {
561 i->setResolveCircularWait();
566 Scan for a speed adjustment change. Find the nearest aircraft that is in front
567 and adjust speed when we get too close. Only do this when current position and/or
568 intentions of the current aircraft match current taxiroute position of the proximate
569 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
570 instruction. See below for the hold position instruction.
572 Note that there currently still is one flaw in the logic that needs to be addressed.
573 can be situations where one aircraft is in front of the current aircraft, on a separate
574 route, but really close after an intersection coming off the current route. This
575 aircraft is still close enough to block the current aircraft. This situation is currently
576 not addressed yet, but should be.
579 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
580 double lon, double heading,
581 double speed, double alt)
584 TrafficVectorIterator current, closest;
585 TrafficVectorIterator i = activeTraffic.begin();
586 bool otherReasonToSlowDown = false;
587 bool previousInstruction;
588 if (activeTraffic.size())
590 //while ((i->getId() != id) && (i != activeTraffic.end()))
591 while (i != activeTraffic.end()) {
592 if (i->getId() == id) {
602 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
603 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
608 previousInstruction = current->getSpeedAdjustment();
609 double mindist = HUGE_VAL;
610 if (activeTraffic.size())
612 double course, dist, bearing, minbearing;
613 SGWayPoint curr (lon,
616 //TrafficVector iterator closest;
618 for (TrafficVectorIterator i = activeTraffic.begin();
619 i != activeTraffic.end(); i++)
622 //SGWayPoint curr (lon,
625 SGWayPoint other (i->getLongitude (),
628 other.CourseAndDistance(curr, &course, &dist);
629 bearing = fabs(heading-course);
631 bearing = 360-bearing;
632 if ((dist < mindist) && (bearing < 60.0))
636 minbearing = bearing;
640 //Check traffic at the tower controller
641 if (towerController->hasActiveTraffic())
643 for (TrafficVectorIterator i = towerController->getActiveTraffic().begin();
644 i != towerController->getActiveTraffic().end(); i++)
646 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
647 //SGWayPoint curr (lon,
650 SGWayPoint other (i->getLongitude (),
653 other.CourseAndDistance(curr, &course, &dist);
654 bearing = fabs(heading-course);
656 bearing = 360-bearing;
657 if ((dist < mindist) && (bearing < 60.0))
661 minbearing = bearing;
662 otherReasonToSlowDown = true;
666 // Finally, check UserPosition
667 double userLatitude = fgGetDouble("/position/latitude-deg");
668 double userLongitude = fgGetDouble("/position/longitude-deg");
669 SGWayPoint user (userLongitude,
671 alt); // Alt is not really important here.
672 user.CourseAndDistance(curr, &course, &dist);
673 bearing = fabs(heading-course);
675 bearing = 360-bearing;
676 if ((dist < mindist) && (bearing < 60.0))
680 minbearing = bearing;
681 otherReasonToSlowDown = true;
684 // if (closest == current) {
685 // //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: closest and current match");
688 //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading
689 // << " course : " << course << endl;
690 current->clearSpeedAdjustment();
692 if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown)
694 double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
695 if (mindist < 2*maxAllowableDistance)
697 if (current->getId() == closest->getWaitsForId())
700 current->setWaitsForId(closest->getId());
701 if (closest->getId() != current->getId())
702 current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
704 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
705 if (mindist < maxAllowableDistance)
707 //double newSpeed = (maxAllowableDistance-mindist);
708 //current->setSpeedAdjustment(newSpeed);
709 //if (mindist < 0.5* maxAllowableDistance)
711 current->setSpeedAdjustment(0);
720 Check for "Hold position instruction".
721 The hold position should be issued under the following conditions:
722 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
723 2) For taxiing aircraft that use one taxiway in opposite directions
724 3) For crossing or merging taxiroutes.
727 void FGGroundNetwork::checkHoldPosition(int id, double lat,
728 double lon, double heading,
729 double speed, double alt)
732 TrafficVectorIterator current;
733 TrafficVectorIterator i = activeTraffic.begin();
734 if (activeTraffic.size())
736 //while ((i->getId() != id) && i != activeTraffic.end())
737 while (i != activeTraffic.end()) {
738 if (i->getId() == id) {
748 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
749 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
752 current->setHoldPosition(false);
753 SGWayPoint curr (lon,
756 double course, dist, bearing, minbearing;
757 for (i = activeTraffic.begin();
758 i != activeTraffic.end(); i++)
760 if (i->getId() != current->getId())
762 int node = current->crosses(this, *i);
765 // Determine whether it's save to continue or not.
766 // If we have a crossing route, there are two possibilities:
767 // 1) This is an interestion
768 // 2) This is oncoming two-way traffic, using the same taxiway.
769 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
770 SGWayPoint nodePos(findNode(node)->getLongitude (),
771 findNode(node)->getLatitude (),
774 SGWayPoint other (i->getLongitude (),
777 //other.CourseAndDistance(curr, &course, &dist);
780 if (current->isOpposing(this, *i, node))
784 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
785 // issue a "Hold Position" as soon as we're close to the offending node
786 // For now, I'm doing this as long as the other aircraft doesn't
787 // have a hold instruction as soon as we're within a reasonable
788 // distance from the offending node.
789 // This may be a bit of a conservative estimate though, as it may
790 // be well possible that both aircraft can both continue to taxi
791 // without crashing into each other.
796 other.CourseAndDistance(nodePos, &course, &dist);
797 if (dist > 200) // 2.0*i->getRadius())
800 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
806 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
809 curr.CourseAndDistance(nodePos, &course, &dist);
810 if (!(i->hasHoldPosition()))
813 if ((dist < 200) && //2.5*current->getRadius()) &&
815 (i->onRoute(this, *current)) &&
816 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
817 (!(current->getId() == i->getWaitsForId())))
818 //(!(i->getSpeedAdjustment()))) // &&
819 //(!(current->getSpeedAdjustment())))
822 current->setHoldPosition(true);
823 current->setWaitsForId(i->getId());
824 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
825 // << dist << " meters. Waiting for " << i->getCallSign();
827 //cerr <<" [opposing] " << endl;
829 // cerr << "[non-opposing] " << endl;
830 //if (i->hasSpeefAdjustment())
832 // cerr << " (which in turn waits for ) " << i->
836 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
845 * Check whether situations occur where the current aircraft is waiting for itself
846 * due to higher order interactions.
847 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
848 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
849 * through this list of waiting aircraft, we can check if we'd eventually end back
850 * at the current aircraft.
852 * Note that we should consider the situation where we are actually checking aircraft
853 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
854 * the looping aircraft. If we don't check for that, this function will get stuck into
858 bool FGGroundNetwork::checkForCircularWaits(int id)
860 //cerr << "Performing Wait check " << id << endl;
862 TrafficVectorIterator current, other;
863 TrafficVectorIterator i = activeTraffic.begin();
864 int trafficSize = activeTraffic.size();
866 while (i != activeTraffic.end()) {
867 if (i->getId() == id) {
876 if (i == activeTraffic.end() || (trafficSize == 0)) {
877 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
881 target = current->getWaitsForId();
882 //bool printed = false; // Note that this variable is for debugging purposes only.
886 //cerr << "aircraft waits for user" << endl;
891 while ((target > 0) && (target != id) && counter++ < trafficSize) {
893 TrafficVectorIterator i = activeTraffic.begin();
895 //while ((i->getId() != id) && i != activeTraffic.end())
896 while (i != activeTraffic.end()) {
897 if (i->getId() == target) {
906 if (i == activeTraffic.end() || (trafficSize == 0)) {
907 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
908 // The target id is not found on the current network, which means it's at the tower
909 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
913 target = other->getWaitsForId();
915 // actually this trap isn't as impossible as it first seemed:
916 // the setWaitsForID(id) is set to current when the aircraft
917 // is waiting for the user controlled aircraft.
918 //if (current->getId() == other->getId()) {
919 // cerr << "Caught the impossible trap" << endl;
920 // cerr << "Current = " << current->getId() << endl;
921 // cerr << "Other = " << other ->getId() << endl;
922 // for (TrafficVectorIterator at = activeTraffic.begin();
923 // at != activeTraffic.end();
925 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
928 if (current->getId() == other->getId())
931 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
932 // << " (" << other->getId() << "); " << endl;;
942 // cerr << "[done] " << endl << endl;;
944 SG_LOG(SG_GENERAL, SG_WARN, "Detected circular wait condition: Id = " << id << "target = " << target);
951 // Note that this function is probably obsolete...
952 bool FGGroundNetwork::hasInstruction(int id)
954 TrafficVectorIterator i = activeTraffic.begin();
955 // Search search if the current id has an entry
956 // This might be faster using a map instead of a vector, but let's start by taking a safe route
957 if (activeTraffic.size())
959 //while ((i->getId() != id) && i != activeTraffic.end()) {
960 while (i != activeTraffic.end()) {
961 if (i->getId() == id) {
967 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
968 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
970 return i->hasInstruction();
975 FGATCInstruction FGGroundNetwork::getInstruction(int id)
977 TrafficVectorIterator i = activeTraffic.begin();
978 // Search search if the current id has an entry
979 // This might be faster using a map instead of a vector, but let's start by taking a safe route
980 if (activeTraffic.size()) {
981 //while ((i->getId() != id) && i != activeTraffic.end()) {
982 while (i != activeTraffic.end()) {
983 if (i->getId() == id) {
989 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
990 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
992 return i->getInstruction();
994 return FGATCInstruction();