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;
315 int FGGroundNetwork::findNearestNode(const SGGeod& aGeod)
317 return findNearestNode(aGeod.getLatitudeDeg(), aGeod.getLongitudeDeg());
320 int FGGroundNetwork::findNearestNode(double lat, double lon)
322 double minDist = HUGE_VAL;
325 SGWayPoint first (lon,
329 for (FGTaxiNodeVectorIterator
331 itr != nodes.end(); itr++)
334 SGWayPoint second ((*itr)->getLongitude(),
335 (*itr)->getLatitude(),
337 first.CourseAndDistance(second, &course, &dist);
341 index = (*itr)->getIndex();
342 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
348 FGTaxiNode *FGGroundNetwork::findNode(int idx)
350 for (FGTaxiNodeVectorIterator
352 itr != nodes.end(); itr++)
354 if (itr->getIndex() == idx)
355 return itr->getAddress();
358 if ((idx >= 0) && (idx < nodes.size()))
359 return nodes[idx]->getAddress();
364 FGTaxiSegment *FGGroundNetwork::findSegment(int idx)
366 for (FGTaxiSegmentVectorIterator
367 itr = segments.begin();
368 itr != segments.end(); itr++)
370 if (itr->getIndex() == idx)
371 return itr->getAddress();
374 if ((idx > 0) && (idx <= segments.size()))
375 return segments[idx-1]->getAddress();
378 //cerr << "Alert: trying to find invalid segment " << idx << endl;
384 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, bool fullSearch)
386 //implements Dijkstra's algorithm to find shortest distance route from start to end
387 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
389 //double INFINITE = 100000000000.0;
390 // initialize scoring values
391 int nParkings = parent->getDynamics()->getNrOfParkings();
392 FGTaxiNodeVector *currNodesSet;
394 currNodesSet = &nodes;
396 currNodesSet = &pushBackNodes;
399 for (FGTaxiNodeVectorIterator
400 itr = currNodesSet->begin();
401 itr != currNodesSet->end(); itr++) {
402 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
403 (*itr)->setPreviousNode(0); //
404 (*itr)->setPreviousSeg (0); //
407 FGTaxiNode *firstNode = findNode(start);
408 firstNode->setPathScore(0);
410 FGTaxiNode *lastNode = findNode(end);
412 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
414 while (!unvisited.empty()) {
415 FGTaxiNode* best = *(unvisited.begin());
416 for (FGTaxiNodeVectorIterator
417 itr = unvisited.begin();
418 itr != unvisited.end(); itr++) {
419 if ((*itr)->getPathScore() < best->getPathScore())
423 FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best);
424 unvisited.erase(newend, unvisited.end());
426 if (best == lastNode) { // found route or best not connected
429 for (FGTaxiSegmentVectorIterator
430 seg = best->getBeginRoute();
431 seg != best->getEndRoute(); seg++) {
432 if (fullSearch || (*seg)->isPushBack()) {
433 FGTaxiNode* tgt = (*seg)->getEnd();
434 double alt = best->getPathScore() + (*seg)->getLength() + (*seg)->getPenalty(nParkings);
435 if (alt < tgt->getPathScore()) { // Relax (u,v)
436 tgt->setPathScore(alt);
437 tgt->setPreviousNode(best);
438 tgt->setPreviousSeg(*seg); //
441 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
447 if (lastNode->getPathScore() == HUGE_VAL) {
448 // no valid route found
450 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " <<
455 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
457 // assemble route from backtrace information
458 intVec nodes, routes;
459 FGTaxiNode* bt = lastNode;
460 while (bt->getPreviousNode() != 0) {
461 nodes.push_back(bt->getIndex());
462 routes.push_back(bt->getPreviousSegment()->getIndex());
463 bt = bt->getPreviousNode();
465 nodes.push_back(start);
466 reverse(nodes.begin(), nodes.end());
467 reverse(routes.begin(), routes.end());
469 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
473 int FGTaxiSegment::getPenalty(int nGates) {
475 if (end->getIndex() < nGates) {
478 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
484 /* ATC Related Functions */
486 void FGGroundNetwork::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition,
487 double lat, double lon, double heading,
488 double speed, double alt, double radius, int leg,
489 FGAIAircraft *aircraft)
491 TrafficVectorIterator i = activeTraffic.begin();
492 // Search search if the current id alread has an entry
493 // This might be faster using a map instead of a vector, but let's start by taking a safe route
494 if (activeTraffic.size()) {
495 //while ((i->getId() != id) && i != activeTraffic.end()) {
496 while (i != activeTraffic.end()) {
497 if (i->getId() == id) {
503 // Add a new TrafficRecord if no one exsists for this aircraft.
504 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
507 rec.setPositionAndIntentions(currentPosition, intendedRoute);
508 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
509 rec.setRadius(radius); // only need to do this when creating the record.
510 rec.setAircraft(aircraft);
511 activeTraffic.push_back(rec);
513 i->setPositionAndIntentions(currentPosition, intendedRoute);
514 i->setPositionAndHeading(lat, lon, heading, speed, alt);
518 void FGGroundNetwork::signOff(int id) {
519 TrafficVectorIterator i = activeTraffic.begin();
520 // Search search if the current id alread has an entry
521 // This might be faster using a map instead of a vector, but let's start by taking a safe route
522 if (activeTraffic.size()) {
523 //while ((i->getId() != id) && i != activeTraffic.end()) {
524 while (i != activeTraffic.end()) {
525 if (i->getId() == id) {
531 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
532 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off");
534 i = activeTraffic.erase(i);
538 void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt,
540 TrafficVectorIterator i = activeTraffic.begin();
541 // Search search if the current id has an entry
542 // This might be faster using a map instead of a vector, but let's start by taking a safe route
543 TrafficVectorIterator current, closest;
544 if (activeTraffic.size()) {
545 //while ((i->getId() != id) && i != activeTraffic.end()) {
546 while (i != activeTraffic.end()) {
547 if (i->getId() == id) {
553 // update position of the current aircraft
554 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
555 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record");
557 i->setPositionAndHeading(lat, lon, heading, speed, alt);
563 // Update every three secs, but add some randomness
564 // to prevent all IA objects doing this in synchrony
565 //if (getDt() < (3.0) + (rand() % 10))
569 current->clearResolveCircularWait();
570 current->setWaitsForId(0);
571 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
572 checkHoldPosition (id, lat, lon, heading, speed, alt);
573 if (checkForCircularWaits(id)) {
574 i->setResolveCircularWait();
579 Scan for a speed adjustment change. Find the nearest aircraft that is in front
580 and adjust speed when we get too close. Only do this when current position and/or
581 intentions of the current aircraft match current taxiroute position of the proximate
582 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
583 instruction. See below for the hold position instruction.
585 Note that there currently still is one flaw in the logic that needs to be addressed.
586 can be situations where one aircraft is in front of the current aircraft, on a separate
587 route, but really close after an intersection coming off the current route. This
588 aircraft is still close enough to block the current aircraft. This situation is currently
589 not addressed yet, but should be.
592 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
593 double lon, double heading,
594 double speed, double alt)
597 TrafficVectorIterator current, closest;
598 TrafficVectorIterator i = activeTraffic.begin();
599 bool otherReasonToSlowDown = false;
600 bool previousInstruction;
601 if (activeTraffic.size())
603 //while ((i->getId() != id) && (i != activeTraffic.end()))
604 while (i != activeTraffic.end()) {
605 if (i->getId() == id) {
615 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
616 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
621 previousInstruction = current->getSpeedAdjustment();
622 double mindist = HUGE_VAL;
623 if (activeTraffic.size())
625 double course, dist, bearing, minbearing;
626 SGWayPoint curr (lon,
629 //TrafficVector iterator closest;
631 for (TrafficVectorIterator i = activeTraffic.begin();
632 i != activeTraffic.end(); i++)
635 //SGWayPoint curr (lon,
638 SGWayPoint other (i->getLongitude (),
641 other.CourseAndDistance(curr, &course, &dist);
642 bearing = fabs(heading-course);
644 bearing = 360-bearing;
645 if ((dist < mindist) && (bearing < 60.0))
649 minbearing = bearing;
653 //Check traffic at the tower controller
654 if (towerController->hasActiveTraffic())
656 for (TrafficVectorIterator i = towerController->getActiveTraffic().begin();
657 i != towerController->getActiveTraffic().end(); i++)
659 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
660 //SGWayPoint curr (lon,
663 SGWayPoint other (i->getLongitude (),
666 other.CourseAndDistance(curr, &course, &dist);
667 bearing = fabs(heading-course);
669 bearing = 360-bearing;
670 if ((dist < mindist) && (bearing < 60.0))
674 minbearing = bearing;
675 otherReasonToSlowDown = true;
679 // Finally, check UserPosition
680 double userLatitude = fgGetDouble("/position/latitude-deg");
681 double userLongitude = fgGetDouble("/position/longitude-deg");
682 SGWayPoint user (userLongitude,
684 alt); // Alt is not really important here.
685 user.CourseAndDistance(curr, &course, &dist);
686 bearing = fabs(heading-course);
688 bearing = 360-bearing;
689 if ((dist < mindist) && (bearing < 60.0))
693 minbearing = bearing;
694 otherReasonToSlowDown = true;
697 // if (closest == current) {
698 // //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: closest and current match");
701 //cerr << "Distance : " << dist << " bearing : " << bearing << " heading : " << heading
702 // << " course : " << course << endl;
703 current->clearSpeedAdjustment();
705 if (current->checkPositionAndIntentions(*closest) || otherReasonToSlowDown)
707 double maxAllowableDistance = (1.1*current->getRadius()) + (1.1*closest->getRadius());
708 if (mindist < 2*maxAllowableDistance)
710 if (current->getId() == closest->getWaitsForId())
713 current->setWaitsForId(closest->getId());
714 if (closest->getId() != current->getId())
715 current->setSpeedAdjustment(closest->getSpeed()* (mindist/100));
717 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
718 if (mindist < maxAllowableDistance)
720 //double newSpeed = (maxAllowableDistance-mindist);
721 //current->setSpeedAdjustment(newSpeed);
722 //if (mindist < 0.5* maxAllowableDistance)
724 current->setSpeedAdjustment(0);
733 Check for "Hold position instruction".
734 The hold position should be issued under the following conditions:
735 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
736 2) For taxiing aircraft that use one taxiway in opposite directions
737 3) For crossing or merging taxiroutes.
740 void FGGroundNetwork::checkHoldPosition(int id, double lat,
741 double lon, double heading,
742 double speed, double alt)
745 TrafficVectorIterator current;
746 TrafficVectorIterator i = activeTraffic.begin();
747 if (activeTraffic.size())
749 //while ((i->getId() != id) && i != activeTraffic.end())
750 while (i != activeTraffic.end()) {
751 if (i->getId() == id) {
761 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
762 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
765 current->setHoldPosition(false);
766 SGWayPoint curr (lon,
769 double course, dist, bearing, minbearing;
770 for (i = activeTraffic.begin();
771 i != activeTraffic.end(); i++)
773 if (i->getId() != current->getId())
775 int node = current->crosses(this, *i);
778 // Determine whether it's save to continue or not.
779 // If we have a crossing route, there are two possibilities:
780 // 1) This is an interestion
781 // 2) This is oncoming two-way traffic, using the same taxiway.
782 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
783 SGWayPoint nodePos(findNode(node)->getLongitude (),
784 findNode(node)->getLatitude (),
787 SGWayPoint other (i->getLongitude (),
790 //other.CourseAndDistance(curr, &course, &dist);
793 if (current->isOpposing(this, *i, node))
797 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
798 // issue a "Hold Position" as soon as we're close to the offending node
799 // For now, I'm doing this as long as the other aircraft doesn't
800 // have a hold instruction as soon as we're within a reasonable
801 // distance from the offending node.
802 // This may be a bit of a conservative estimate though, as it may
803 // be well possible that both aircraft can both continue to taxi
804 // without crashing into each other.
809 other.CourseAndDistance(nodePos, &course, &dist);
810 if (dist > 200) // 2.0*i->getRadius())
813 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
819 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
822 curr.CourseAndDistance(nodePos, &course, &dist);
823 if (!(i->hasHoldPosition()))
826 if ((dist < 200) && //2.5*current->getRadius()) &&
828 (i->onRoute(this, *current)) &&
829 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
830 (!(current->getId() == i->getWaitsForId())))
831 //(!(i->getSpeedAdjustment()))) // &&
832 //(!(current->getSpeedAdjustment())))
835 current->setHoldPosition(true);
836 current->setWaitsForId(i->getId());
837 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
838 // << dist << " meters. Waiting for " << i->getCallSign();
840 //cerr <<" [opposing] " << endl;
842 // cerr << "[non-opposing] " << endl;
843 //if (i->hasSpeefAdjustment())
845 // cerr << " (which in turn waits for ) " << i->
849 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
858 * Check whether situations occur where the current aircraft is waiting for itself
859 * due to higher order interactions.
860 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
861 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
862 * through this list of waiting aircraft, we can check if we'd eventually end back
863 * at the current aircraft.
865 * Note that we should consider the situation where we are actually checking aircraft
866 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
867 * the looping aircraft. If we don't check for that, this function will get stuck into
871 bool FGGroundNetwork::checkForCircularWaits(int id)
873 //cerr << "Performing Wait check " << id << endl;
875 TrafficVectorIterator current, other;
876 TrafficVectorIterator i = activeTraffic.begin();
877 int trafficSize = activeTraffic.size();
879 while (i != activeTraffic.end()) {
880 if (i->getId() == id) {
889 if (i == activeTraffic.end() || (trafficSize == 0)) {
890 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
894 target = current->getWaitsForId();
895 //bool printed = false; // Note that this variable is for debugging purposes only.
899 //cerr << "aircraft waits for user" << endl;
904 while ((target > 0) && (target != id) && counter++ < trafficSize) {
906 TrafficVectorIterator i = activeTraffic.begin();
908 //while ((i->getId() != id) && i != activeTraffic.end())
909 while (i != activeTraffic.end()) {
910 if (i->getId() == target) {
919 if (i == activeTraffic.end() || (trafficSize == 0)) {
920 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
921 // The target id is not found on the current network, which means it's at the tower
922 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
926 target = other->getWaitsForId();
928 // actually this trap isn't as impossible as it first seemed:
929 // the setWaitsForID(id) is set to current when the aircraft
930 // is waiting for the user controlled aircraft.
931 //if (current->getId() == other->getId()) {
932 // cerr << "Caught the impossible trap" << endl;
933 // cerr << "Current = " << current->getId() << endl;
934 // cerr << "Other = " << other ->getId() << endl;
935 // for (TrafficVectorIterator at = activeTraffic.begin();
936 // at != activeTraffic.end();
938 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
941 if (current->getId() == other->getId())
944 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
945 // << " (" << other->getId() << "); " << endl;;
955 // cerr << "[done] " << endl << endl;;
957 SG_LOG(SG_GENERAL, SG_WARN, "Detected circular wait condition: Id = " << id << "target = " << target);
964 // Note that this function is probably obsolete...
965 bool FGGroundNetwork::hasInstruction(int id)
967 TrafficVectorIterator i = activeTraffic.begin();
968 // Search search if the current id has an entry
969 // This might be faster using a map instead of a vector, but let's start by taking a safe route
970 if (activeTraffic.size())
972 //while ((i->getId() != id) && i != activeTraffic.end()) {
973 while (i != activeTraffic.end()) {
974 if (i->getId() == id) {
980 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
981 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record");
983 return i->hasInstruction();
988 FGATCInstruction FGGroundNetwork::getInstruction(int id)
990 TrafficVectorIterator i = activeTraffic.begin();
991 // Search search if the current id has an entry
992 // This might be faster using a map instead of a vector, but let's start by taking a safe route
993 if (activeTraffic.size()) {
994 //while ((i->getId() != id) && i != activeTraffic.end()) {
995 while (i != activeTraffic.end()) {
996 if (i->getId() == id) {
1002 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1003 SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record");
1005 return i->getInstruction();
1007 return FGATCInstruction();