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 <Airports/dynamics.hxx>
46 #include <AIModel/AIFlightPlan.hxx>
50 #include "groundnetwork.hxx"
52 /***************************************************************************
54 **************************************************************************/
56 void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
58 FGTaxiNodeVectorIterator i = nodes->begin();
59 while (i != nodes->end())
61 //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
62 if ((*i)->getIndex() == startNode)
64 start = (*i)->getAddress();
65 (*i)->addSegment(this);
70 SG_LOG(SG_GENERAL, SG_ALERT, "Could not find start node " << startNode << endl);
73 void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
75 FGTaxiNodeVectorIterator i = nodes->begin();
76 while (i != nodes->end())
78 //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
79 if ((*i)->getIndex() == endNode)
81 end = (*i)->getAddress();
86 SG_LOG(SG_GENERAL, SG_ALERT, "Could not find end node " << endNode << endl);
91 // There is probably a computationally cheaper way of
93 void FGTaxiSegment::setTrackDistance()
96 SGWayPoint first (start->getLongitude(),
99 SGWayPoint second (end->getLongitude(),
102 first.CourseAndDistance(second, &course, &length);
106 void FGTaxiSegment::setCourseDiff(double crse)
108 headingDiff = fabs(course-crse);
110 if (headingDiff > 180)
111 headingDiff = fabs(headingDiff - 360);
115 /***************************************************************************
117 **************************************************************************/
118 bool FGTaxiRoute::next(int *nde)
120 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
121 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
122 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
123 //if (currNode != nodes.end())
124 // cerr << "true" << endl;
126 // cerr << "false" << endl;
127 //if (nodes.size() != (routes.size()) +1)
128 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
130 if (currNode == nodes.end())
133 if (currNode != nodes.begin()) // make sure route corresponds to the end node
139 bool FGTaxiRoute::next(int *nde, int *rte)
141 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
142 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
143 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
144 //if (currNode != nodes.end())
145 // cerr << "true" << endl;
147 // cerr << "false" << endl;
148 if (nodes.size() != (routes.size()) +1) {
149 SG_LOG(SG_GENERAL, SG_ALERT, "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size());
152 if (currNode == nodes.end())
155 //*rte = *(currRoute);
156 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
163 // If currNode points to the first node, this means the aircraft is not on the taxi node
164 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
165 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
166 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
167 // unique for any starting location.
168 // Note that this is probably just a temporary fix until I get Parking / tower control working.
169 *rte = -1 * *(currRoute);
175 void FGTaxiRoute::rewind(int route)
181 if (!(next(&currPoint, &currRoute))) {
182 SG_LOG(SG_GENERAL,SG_ALERT, "Error in rewinding TaxiRoute: current" << currRoute
183 << " goal " << route);
185 } while (currRoute != route);
191 /***************************************************************************
193 **************************************************************************/
194 bool compare_nodes(FGTaxiNode *a, FGTaxiNode *b) {
198 bool compare_segments(FGTaxiSegment *a, FGTaxiSegment *b) {
202 FGGroundNetwork::FGGroundNetwork()
210 currTraffic = activeTraffic.begin();
214 FGGroundNetwork::~FGGroundNetwork()
216 for (FGTaxiNodeVectorIterator node = nodes.begin();
223 pushBackNodes.clear();
224 for (FGTaxiSegmentVectorIterator seg = segments.begin();
225 seg != segments.end();
233 void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
235 segments.push_back(new FGTaxiSegment(seg));
238 void FGGroundNetwork::addNode(const FGTaxiNode &node)
240 nodes.push_back(new FGTaxiNode(node));
243 void FGGroundNetwork::addNodes(FGParkingVec *parkings)
246 FGParkingVecIterator i = parkings->begin();
247 while (i != parkings->end())
249 n.setIndex(i->getIndex());
250 n.setLatitude(i->getLatitude());
251 n.setLongitude(i->getLongitude());
252 nodes.push_back(new FGTaxiNode(n));
260 void FGGroundNetwork::init()
264 sort(nodes.begin(), nodes.end(), compare_nodes);
265 //sort(segments.begin(), segments.end(), compare_segments());
266 FGTaxiSegmentVectorIterator i = segments.begin();
267 while(i != segments.end()) {
268 (*i)->setStart(&nodes);
269 (*i)->setEnd (&nodes);
270 (*i)->setTrackDistance();
271 (*i)->setIndex(index);
272 if ((*i)->isPushBack()) {
273 pushBackNodes.push_back((*i)->getEnd());
275 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
276 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
277 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
278 // << (*i)->getEnd()->getIndex() << endl);
283 i = segments.begin();
284 while(i != segments.end()) {
285 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
286 while (j != (*i)->getEnd()->getEndRoute())
288 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex())
290 int start1 = (*i)->getStart()->getIndex();
291 int end1 = (*i)->getEnd() ->getIndex();
292 int start2 = (*j)->getStart()->getIndex();
293 int end2 = (*j)->getEnd()->getIndex();
294 int oppIndex = (*j)->getIndex();
295 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
296 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
297 (*i)->setOpposite(*j);
304 //FGTaxiNodeVectorIterator j = nodes.begin();
305 //while (j != nodes.end()) {
306 // if ((*j)->getHoldPointType() == 3) {
307 // pushBackNodes.push_back((*j));
311 //cerr << "Done initializing ground network" << endl;
317 int FGGroundNetwork::findNearestNode(double lat, double lon)
319 double minDist = HUGE_VAL;
322 SGWayPoint first (lon,
326 for (FGTaxiNodeVectorIterator
328 itr != nodes.end(); itr++)
331 SGWayPoint second ((*itr)->getLongitude(),
332 (*itr)->getLatitude(),
334 first.CourseAndDistance(second, &course, &dist);
338 index = (*itr)->getIndex();
339 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
345 FGTaxiNode *FGGroundNetwork::findNode(int idx)
347 for (FGTaxiNodeVectorIterator
349 itr != nodes.end(); itr++)
351 if (itr->getIndex() == idx)
352 return itr->getAddress();
355 if ((idx >= 0) && (idx < nodes.size()))
356 return nodes[idx]->getAddress();
361 FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
363 for (FGTaxiSegmentVectorIterator
364 itr = segments.begin();
365 itr != segments.end(); itr++)
367 if (itr->getIndex() == idx)
368 return itr->getAddress();
371 if ((idx > 0) && (idx <= segments.size()))
372 return segments[idx-1]->getAddress();
375 //cerr << "Alert: trying to find invalid segment " << idx << endl;
381 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, bool fullSearch)
383 //implements Dijkstra's algorithm to find shortest distance route from start to end
384 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
386 //double INFINITE = 100000000000.0;
387 // initialize scoring values
388 int nParkings = parent->getDynamics()->getNrOfParkings();
389 FGTaxiNodeVector *currNodesSet;
391 currNodesSet = &nodes;
393 currNodesSet = &pushBackNodes;
396 for (FGTaxiNodeVectorIterator
397 itr = currNodesSet->begin();
398 itr != currNodesSet->end(); itr++) {
399 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
400 (*itr)->setPreviousNode(0); //
401 (*itr)->setPreviousSeg (0); //
404 FGTaxiNode *firstNode = findNode(start);
405 firstNode->setPathScore(0);
407 FGTaxiNode *lastNode = findNode(end);
409 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
411 while (!unvisited.empty()) {
412 FGTaxiNode* best = *(unvisited.begin());
413 for (FGTaxiNodeVectorIterator
414 itr = unvisited.begin();
415 itr != unvisited.end(); itr++) {
416 if ((*itr)->getPathScore() < best->getPathScore())
420 FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best);
421 unvisited.erase(newend, unvisited.end());
423 if (best == lastNode) { // found route or best not connected
426 for (FGTaxiSegmentVectorIterator
427 seg = best->getBeginRoute();
428 seg != best->getEndRoute(); seg++) {
429 if (fullSearch || (*seg)->isPushBack()) {
430 FGTaxiNode* tgt = (*seg)->getEnd();
431 double alt = best->getPathScore() + (*seg)->getLength() + (*seg)->getPenalty(nParkings);
432 if (alt < tgt->getPathScore()) { // Relax (u,v)
433 tgt->setPathScore(alt);
434 tgt->setPreviousNode(best);
435 tgt->setPreviousSeg(*seg); //
438 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
444 if (lastNode->getPathScore() == HUGE_VAL) {
445 // no valid route found
447 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " <<
452 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
454 // assemble route from backtrace information
455 intVec nodes, routes;
456 FGTaxiNode* bt = lastNode;
457 while (bt->getPreviousNode() != 0) {
458 nodes.push_back(bt->getIndex());
459 routes.push_back(bt->getPreviousSegment()->getIndex());
460 bt = bt->getPreviousNode();
462 nodes.push_back(start);
463 reverse(nodes.begin(), nodes.end());
464 reverse(routes.begin(), routes.end());
466 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
470 int FGTaxiSegment::getPenalty(int nGates) {
472 if (end->getIndex() < nGates) {
475 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
481 /* ATC Related Functions */
483 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
484 double lat, double lon, double heading,
485 double speed, double alt, double radius, int leg,
486 FGAIAircraft *aircraft)
488 TrafficVectorIterator i = activeTraffic.begin();
489 // Search search if the current id alread has an entry
490 // This might be faster using a map instead of a vector, but let's start by taking a safe route
491 if (activeTraffic.size()) {
492 //while ((i->getId() != id) && i != activeTraffic.end()) {
493 while (i != activeTraffic.end()) {
494 if (i->getId() == id) {
500 // Add a new TrafficRecord if no one exsists for this aircraft.
501 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
504 rec.setPositionAndIntentions(currentPosition, intendedRoute);
505 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
506 rec.setRadius(radius); // only need to do this when creating the record.
507 rec.setAircraft(aircraft);
508 activeTraffic.push_back(rec);
510 i->setPositionAndIntentions(currentPosition, intendedRoute);
511 i->setPositionAndHeading(lat, lon, heading, speed, alt);
515 void FGGroundNetwork::signOff(int id) {
516 TrafficVectorIterator i = activeTraffic.begin();
517 // Search search if the current id alread has an entry
518 // This might be faster using a map instead of a vector, but let's start by taking a safe route
519 if (activeTraffic.size()) {
520 //while ((i->getId() != id) && i != activeTraffic.end()) {
521 while (i != activeTraffic.end()) {
522 if (i->getId() == id) {
528 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
529 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
531 i = activeTraffic.erase(i);
535 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt,
537 TrafficVectorIterator i = activeTraffic.begin();
538 // Search search if the current id has an entry
539 // This might be faster using a map instead of a vector, but let's start by taking a safe route
540 TrafficVectorIterator current, closest;
541 if (activeTraffic.size()) {
542 //while ((i->getId() != id) && i != activeTraffic.end()) {
543 while (i != activeTraffic.end()) {
544 if (i->getId() == id) {
550 // update position of the current aircraft
551 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
552 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
554 i->setPositionAndHeading(lat, lon, heading, speed, alt);
560 // Update every three secs, but add some randomness
561 // to prevent all IA objects doing this in synchrony
562 //if (getDt() < (3.0) + (rand() % 10))
566 current->clearResolveCircularWait();
567 current->setWaitsForId(0);
568 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
569 checkHoldPosition (id, lat, lon, heading, speed, alt);
570 if (checkForCircularWaits(id)) {
571 i->setResolveCircularWait();
576 Scan for a speed adjustment change. Find the nearest aircraft that is in front
577 and adjust speed when we get too close. Only do this when current position and/or
578 intentions of the current aircraft match current taxiroute position of the proximate
579 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
580 instruction. See below for the hold position instruction.
582 Note that there currently still is one flaw in the logic that needs to be addressed.
583 can be situations where one aircraft is in front of the current aircraft, on a separate
584 route, but really close after an intersection coming off the current route. This
585 aircraft is still close enough to block the current aircraft. This situation is currently
586 not addressed yet, but should be.
589 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
590 double lon, double heading,
591 double speed, double alt)
594 TrafficVectorIterator current, closest;
595 TrafficVectorIterator i = activeTraffic.begin();
596 bool otherReasonToSlowDown = false;
597 bool previousInstruction;
598 if (activeTraffic.size())
600 //while ((i->getId() != id) && (i != activeTraffic.end()))
601 while (i != activeTraffic.end()) {
602 if (i->getId() == id) {
612 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
613 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
618 previousInstruction = current->getSpeedAdjustment();
619 double mindist = HUGE_VAL;
620 if (activeTraffic.size())
622 double course, dist, bearing, minbearing;
623 SGWayPoint curr (lon,
626 //TrafficVector iterator closest;
628 for (TrafficVectorIterator i = activeTraffic.begin();
629 i != activeTraffic.end(); i++)
632 //SGWayPoint curr (lon,
635 SGWayPoint other (i->getLongitude (),
638 other.CourseAndDistance(curr, &course, &dist);
639 bearing = fabs(heading-course);
641 bearing = 360-bearing;
642 if ((dist < mindist) && (bearing < 60.0))
646 minbearing = bearing;
650 //Check traffic at the tower controller
651 if (towerController->hasActiveTraffic())
653 for (TrafficVectorIterator i = towerController->getActiveTraffic().begin();
654 i != towerController->getActiveTraffic().end(); i++)
656 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
657 //SGWayPoint curr (lon,
660 SGWayPoint other (i->getLongitude (),
663 other.CourseAndDistance(curr, &course, &dist);
664 bearing = fabs(heading-course);
666 bearing = 360-bearing;
667 if ((dist < mindist) && (bearing < 60.0))
671 minbearing = bearing;
672 otherReasonToSlowDown = true;
676 // Finally, check UserPosition
677 double userLatitude = fgGetDouble("/position/latitude-deg");
678 double userLongitude = fgGetDouble("/position/longitude-deg");
679 SGWayPoint user (userLongitude,
681 alt); // Alt is not really important here.
682 user.CourseAndDistance(curr, &course, &dist);
683 bearing = fabs(heading-course);
685 bearing = 360-bearing;
686 if ((dist < mindist) && (bearing < 60.0))
690 minbearing = bearing;
691 otherReasonToSlowDown = true;
694 // if (closest == current) {
695 // //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: closest and current match");
698 //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading
699 // << " course : " << course << endl;
700 current->clearSpeedAdjustment();
702 if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown)
704 double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
705 if (mindist < 2*maxAllowableDistance)
707 if (current->getId() == closest->getWaitsForId())
710 current->setWaitsForId(closest->getId());
711 if (closest->getId() != current->getId())
712 current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
714 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
715 if (mindist < maxAllowableDistance)
717 //double newSpeed = (maxAllowableDistance-mindist);
718 //current->setSpeedAdjustment(newSpeed);
719 //if (mindist < 0.5* maxAllowableDistance)
721 current->setSpeedAdjustment(0);
730 Check for "Hold position instruction".
731 The hold position should be issued under the following conditions:
732 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
733 2) For taxiing aircraft that use one taxiway in opposite directions
734 3) For crossing or merging taxiroutes.
737 void FGGroundNetwork::checkHoldPosition(int id, double lat,
738 double lon, double heading,
739 double speed, double alt)
742 TrafficVectorIterator current;
743 TrafficVectorIterator i = activeTraffic.begin();
744 if (activeTraffic.size())
746 //while ((i->getId() != id) && i != activeTraffic.end())
747 while (i != activeTraffic.end()) {
748 if (i->getId() == id) {
758 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
759 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
762 current->setHoldPosition(false);
763 SGWayPoint curr (lon,
766 double course, dist, bearing, minbearing;
767 for (i = activeTraffic.begin();
768 i != activeTraffic.end(); i++)
770 if (i->getId() != current->getId())
772 int node = current->crosses(this, *i);
775 // Determine whether it's save to continue or not.
776 // If we have a crossing route, there are two possibilities:
777 // 1) This is an interestion
778 // 2) This is oncoming two-way traffic, using the same taxiway.
779 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
780 SGWayPoint nodePos(findNode(node)->getLongitude (),
781 findNode(node)->getLatitude (),
784 SGWayPoint other (i->getLongitude (),
787 //other.CourseAndDistance(curr, &course, &dist);
790 if (current->isOpposing(this, *i, node))
794 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
795 // issue a "Hold Position" as soon as we're close to the offending node
796 // For now, I'm doing this as long as the other aircraft doesn't
797 // have a hold instruction as soon as we're within a reasonable
798 // distance from the offending node.
799 // This may be a bit of a conservative estimate though, as it may
800 // be well possible that both aircraft can both continue to taxi
801 // without crashing into each other.
806 other.CourseAndDistance(nodePos, &course, &dist);
807 if (dist > 200) // 2.0*i->getRadius())
810 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
816 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
819 curr.CourseAndDistance(nodePos, &course, &dist);
820 if (!(i->hasHoldPosition()))
823 if ((dist < 200) && //2.5*current->getRadius()) &&
825 (i->onRoute(this, *current)) &&
826 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
827 (!(current->getId() == i->getWaitsForId())))
828 //(!(i->getSpeedAdjustment()))) // &&
829 //(!(current->getSpeedAdjustment())))
832 current->setHoldPosition(true);
833 current->setWaitsForId(i->getId());
834 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
835 // << dist << " meters. Waiting for " << i->getCallSign();
837 //cerr <<" [opposing] " << endl;
839 // cerr << "[non-opposing] " << endl;
840 //if (i->hasSpeefAdjustment())
842 // cerr << " (which in turn waits for ) " << i->
846 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
855 * Check whether situations occur where the current aircraft is waiting for itself
856 * due to higher order interactions.
857 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
858 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
859 * through this list of waiting aircraft, we can check if we'd eventually end back
860 * at the current aircraft.
862 * Note that we should consider the situation where we are actually checking aircraft
863 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
864 * the looping aircraft. If we don't check for that, this function will get stuck into
868 bool FGGroundNetwork::checkForCircularWaits(int id)
870 //cerr << "Performing Wait check " << id << endl;
872 TrafficVectorIterator current, other;
873 TrafficVectorIterator i = activeTraffic.begin();
874 int trafficSize = activeTraffic.size();
876 while (i != activeTraffic.end()) {
877 if (i->getId() == id) {
886 if (i == activeTraffic.end() || (trafficSize == 0)) {
887 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
891 target = current->getWaitsForId();
892 //bool printed = false; // Note that this variable is for debugging purposes only.
896 //cerr << "aircraft waits for user" << endl;
901 while ((target > 0) && (target != id) && counter++ < trafficSize) {
903 TrafficVectorIterator i = activeTraffic.begin();
905 //while ((i->getId() != id) && i != activeTraffic.end())
906 while (i != activeTraffic.end()) {
907 if (i->getId() == target) {
916 if (i == activeTraffic.end() || (trafficSize == 0)) {
917 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
918 // The target id is not found on the current network, which means it's at the tower
919 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
923 target = other->getWaitsForId();
925 // actually this trap isn't as impossible as it first seemed:
926 // the setWaitsForID(id) is set to current when the aircraft
927 // is waiting for the user controlled aircraft.
928 //if (current->getId() == other->getId()) {
929 // cerr << "Caught the impossible trap" << endl;
930 // cerr << "Current = " << current->getId() << endl;
931 // cerr << "Other = " << other ->getId() << endl;
932 // for (TrafficVectorIterator at = activeTraffic.begin();
933 // at != activeTraffic.end();
935 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
938 if (current->getId() == other->getId())
941 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
942 // << " (" << other->getId() << "); " << endl;;
952 // cerr << "[done] " << endl << endl;;
954 SG_LOG(SG_GENERAL, SG_WARN, "Detected circular wait condition: Id = " << id << "target = " << target);
961 // Note that this function is probably obsolete...
962 bool FGGroundNetwork::hasInstruction(int id)
964 TrafficVectorIterator i = activeTraffic.begin();
965 // Search search if the current id has an entry
966 // This might be faster using a map instead of a vector, but let's start by taking a safe route
967 if (activeTraffic.size())
969 //while ((i->getId() != id) && i != activeTraffic.end()) {
970 while (i != activeTraffic.end()) {
971 if (i->getId() == id) {
977 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
978 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
980 return i->hasInstruction();
985 FGATCInstruction FGGroundNetwork::getInstruction(int id)
987 TrafficVectorIterator i = activeTraffic.begin();
988 // Search search if the current id has an entry
989 // This might be faster using a map instead of a vector, but let's start by taking a safe route
990 if (activeTraffic.size()) {
991 //while ((i->getId() != id) && i != activeTraffic.end()) {
992 while (i != activeTraffic.end()) {
993 if (i->getId() == id) {
999 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1000 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
1002 return i->getInstruction();
1004 return FGATCInstruction();