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/AIAircraft.hxx>
36 #include <AIModel/AIFlightPlan.hxx>
38 #include "groundnetwork.hxx"
40 /***************************************************************************
42 **************************************************************************/
44 void FGTaxiSegment::setStart(FGTaxiNodeVector * nodes)
46 FGTaxiNodeVectorIterator i = nodes->begin();
47 while (i != nodes->end()) {
48 //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
49 if ((*i)->getIndex() == startNode) {
50 start = (*i)->getAddress();
51 (*i)->addSegment(this);
56 SG_LOG(SG_GENERAL, SG_ALERT,
57 "Could not find start node " << startNode << endl);
60 void FGTaxiSegment::setEnd(FGTaxiNodeVector * nodes)
62 FGTaxiNodeVectorIterator i = nodes->begin();
63 while (i != nodes->end()) {
64 //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
65 if ((*i)->getIndex() == endNode) {
66 end = (*i)->getAddress();
71 SG_LOG(SG_GENERAL, SG_ALERT,
72 "Could not find end node " << endNode << endl);
77 // There is probably a computationally cheaper way of
79 void FGTaxiSegment::setTrackDistance()
81 length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
85 void FGTaxiSegment::setCourseDiff(double crse)
87 headingDiff = fabs(course - crse);
89 if (headingDiff > 180)
90 headingDiff = fabs(headingDiff - 360);
94 /***************************************************************************
96 **************************************************************************/
97 bool FGTaxiRoute::next(int *nde)
99 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
100 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
101 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
102 //if (currNode != nodes.end())
103 // cerr << "true" << endl;
105 // cerr << "false" << endl;
106 //if (nodes.size() != (routes.size()) +1)
107 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
109 if (currNode == nodes.end())
112 if (currNode != nodes.begin()) // make sure route corresponds to the end node
118 bool FGTaxiRoute::next(int *nde, int *rte)
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 SG_LOG(SG_GENERAL, SG_ALERT,
129 "ALERT: Misconfigured TaxiRoute : " << nodes.
130 size() << " " << routes.size());
133 if (currNode == nodes.end())
136 //*rte = *(currRoute);
137 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
142 // If currNode points to the first node, this means the aircraft is not on the taxi node
143 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
144 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
145 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
146 // unique for any starting location.
147 // Note that this is probably just a temporary fix until I get Parking / tower control working.
148 *rte = -1 * *(currRoute);
154 void FGTaxiRoute::rewind(int route)
160 if (!(next(&currPoint, &currRoute))) {
161 SG_LOG(SG_GENERAL, SG_ALERT,
162 "Error in rewinding TaxiRoute: current" << currRoute <<
165 } while (currRoute != route);
171 /***************************************************************************
173 **************************************************************************/
174 bool compare_nodes(FGTaxiNode * a, FGTaxiNode * b)
179 bool compare_segments(FGTaxiSegment * a, FGTaxiSegment * b)
184 FGGroundNetwork::FGGroundNetwork()
192 currTraffic = activeTraffic.begin();
196 FGGroundNetwork::~FGGroundNetwork()
198 for (FGTaxiNodeVectorIterator node = nodes.begin();
199 node != nodes.end(); node++) {
203 pushBackNodes.clear();
204 for (FGTaxiSegmentVectorIterator seg = segments.begin();
205 seg != segments.end(); seg++) {
211 void FGGroundNetwork::addSegment(const FGTaxiSegment & seg)
213 segments.push_back(new FGTaxiSegment(seg));
216 void FGGroundNetwork::addNode(const FGTaxiNode & node)
218 nodes.push_back(new FGTaxiNode(node));
221 void FGGroundNetwork::addNodes(FGParkingVec * parkings)
224 FGParkingVecIterator i = parkings->begin();
225 while (i != parkings->end()) {
226 n.setIndex(i->getIndex());
227 n.setLatitude(i->getLatitude());
228 n.setLongitude(i->getLongitude());
229 nodes.push_back(new FGTaxiNode(n));
237 void FGGroundNetwork::init()
241 sort(nodes.begin(), nodes.end(), compare_nodes);
242 //sort(segments.begin(), segments.end(), compare_segments());
243 FGTaxiSegmentVectorIterator i = segments.begin();
244 while (i != segments.end()) {
245 (*i)->setStart(&nodes);
246 (*i)->setEnd(&nodes);
247 (*i)->setTrackDistance();
248 (*i)->setIndex(index);
249 if ((*i)->isPushBack()) {
250 pushBackNodes.push_back((*i)->getEnd());
252 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
253 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
254 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
255 // << (*i)->getEnd()->getIndex() << endl);
260 i = segments.begin();
261 while (i != segments.end()) {
262 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
263 while (j != (*i)->getEnd()->getEndRoute()) {
264 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex()) {
265 // int start1 = (*i)->getStart()->getIndex();
266 // int end1 = (*i)->getEnd() ->getIndex();
267 // int start2 = (*j)->getStart()->getIndex();
268 // int end2 = (*j)->getEnd()->getIndex();
269 // int oppIndex = (*j)->getIndex();
270 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
271 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
272 (*i)->setOpposite(*j);
279 //FGTaxiNodeVectorIterator j = nodes.begin();
280 //while (j != nodes.end()) {
281 // if ((*j)->getHoldPointType() == 3) {
282 // pushBackNodes.push_back((*j));
286 //cerr << "Done initializing ground network" << endl;
290 int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
292 double minDist = HUGE_VAL;
295 for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
297 double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
300 index = (*itr)->getIndex();
301 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
308 int FGGroundNetwork::findNearestNode(double lat, double lon)
310 return findNearestNode(SGGeod::fromDeg(lon, lat));
313 FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
315 for (FGTaxiNodeVectorIterator
317 itr != nodes.end(); itr++)
319 if (itr->getIndex() == idx)
320 return itr->getAddress();
323 if ((idx >= 0) && (idx < nodes.size()))
324 return nodes[idx]->getAddress();
329 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
331 for (FGTaxiSegmentVectorIterator
332 itr = segments.begin();
333 itr != segments.end(); itr++)
335 if (itr->getIndex() == idx)
336 return itr->getAddress();
339 if ((idx > 0) && (idx <= segments.size()))
340 return segments[idx - 1]->getAddress();
342 //cerr << "Alert: trying to find invalid segment " << idx << endl;
348 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
351 //implements Dijkstra's algorithm to find shortest distance route from start to end
352 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
354 //double INFINITE = 100000000000.0;
355 // initialize scoring values
356 int nParkings = parent->getDynamics()->getNrOfParkings();
357 FGTaxiNodeVector *currNodesSet;
359 currNodesSet = &nodes;
361 currNodesSet = &pushBackNodes;
364 for (FGTaxiNodeVectorIterator
365 itr = currNodesSet->begin(); itr != currNodesSet->end(); itr++) {
366 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
367 (*itr)->setPreviousNode(0); //
368 (*itr)->setPreviousSeg(0); //
371 FGTaxiNode *firstNode = findNode(start);
372 firstNode->setPathScore(0);
374 FGTaxiNode *lastNode = findNode(end);
376 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
378 while (!unvisited.empty()) {
379 FGTaxiNode *best = *(unvisited.begin());
380 for (FGTaxiNodeVectorIterator
381 itr = unvisited.begin(); itr != unvisited.end(); itr++) {
382 if ((*itr)->getPathScore() < best->getPathScore())
386 FGTaxiNodeVectorIterator newend =
387 remove(unvisited.begin(), unvisited.end(), best);
388 unvisited.erase(newend, unvisited.end());
390 if (best == lastNode) { // found route or best not connected
393 for (FGTaxiSegmentVectorIterator
394 seg = best->getBeginRoute();
395 seg != best->getEndRoute(); seg++) {
396 if (fullSearch || (*seg)->isPushBack()) {
397 FGTaxiNode *tgt = (*seg)->getEnd();
399 best->getPathScore() + (*seg)->getLength() +
400 (*seg)->getPenalty(nParkings);
401 if (alt < tgt->getPathScore()) { // Relax (u,v)
402 tgt->setPathScore(alt);
403 tgt->setPreviousNode(best);
404 tgt->setPreviousSeg(*seg); //
407 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
413 if (lastNode->getPathScore() == HUGE_VAL) {
414 // no valid route found
416 SG_LOG(SG_GENERAL, SG_ALERT,
417 "Failed to find route from waypoint " << start << " to "
418 << end << " at " << parent->getId());
422 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
424 // assemble route from backtrace information
425 intVec nodes, routes;
426 FGTaxiNode *bt = lastNode;
427 while (bt->getPreviousNode() != 0) {
428 nodes.push_back(bt->getIndex());
429 routes.push_back(bt->getPreviousSegment()->getIndex());
430 bt = bt->getPreviousNode();
432 nodes.push_back(start);
433 reverse(nodes.begin(), nodes.end());
434 reverse(routes.begin(), routes.end());
436 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
440 int FGTaxiSegment::getPenalty(int nGates)
443 if (end->getIndex() < nGates) {
446 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
452 /* ATC Related Functions */
454 void FGGroundNetwork::announcePosition(int id,
455 FGAIFlightPlan * intendedRoute,
456 int currentPosition, double lat,
457 double lon, double heading,
458 double speed, double alt,
459 double radius, int leg,
460 FGAIAircraft * aircraft)
462 TrafficVectorIterator i = activeTraffic.begin();
463 // Search search if the current id alread has an entry
464 // This might be faster using a map instead of a vector, but let's start by taking a safe route
465 if (activeTraffic.size()) {
466 //while ((i->getId() != id) && i != activeTraffic.end()) {
467 while (i != activeTraffic.end()) {
468 if (i->getId() == id) {
474 // Add a new TrafficRecord if no one exsists for this aircraft.
475 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
479 rec.setPositionAndIntentions(currentPosition, intendedRoute);
480 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
481 rec.setRadius(radius); // only need to do this when creating the record.
482 rec.setAircraft(aircraft);
483 activeTraffic.push_back(rec);
485 i->setPositionAndIntentions(currentPosition, intendedRoute);
486 i->setPositionAndHeading(lat, lon, heading, speed, alt);
490 void FGGroundNetwork::signOff(int id)
492 TrafficVectorIterator i = activeTraffic.begin();
493 // Search search if the current id alread has an entry
494 // This might be faster using a map instead of a vector, but let's start by taking a safe route
495 if (activeTraffic.size()) {
496 //while ((i->getId() != id) && i != activeTraffic.end()) {
497 while (i != activeTraffic.end()) {
498 if (i->getId() == id) {
504 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
505 SG_LOG(SG_GENERAL, SG_ALERT,
506 "AI error: Aircraft without traffic record is signing off");
508 i = activeTraffic.erase(i);
512 void FGGroundNetwork::update(int id, double lat, double lon,
513 double heading, double speed, double alt,
516 // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to
517 // Transmit air-to-ground "Ready to taxi request:
518 // Transmit ground to air approval / hold
519 // Transmit confirmation ...
520 // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
523 TrafficVectorIterator i = activeTraffic.begin();
524 // Search search if the current id has an entry
525 // This might be faster using a map instead of a vector, but let's start by taking a safe route
526 TrafficVectorIterator current, closest;
527 if (activeTraffic.size()) {
528 //while ((i->getId() != id) && i != activeTraffic.end()) {
529 while (i != activeTraffic.end()) {
530 if (i->getId() == id) {
536 // update position of the current aircraft
537 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
538 SG_LOG(SG_GENERAL, SG_ALERT,
539 "AI error: updating aircraft without traffic record");
541 i->setPositionAndHeading(lat, lon, heading, speed, alt);
547 // Update every three secs, but add some randomness
548 // to prevent all IA objects doing this in synchrony
549 //if (getDt() < (3.0) + (rand() % 10))
553 current->clearResolveCircularWait();
554 current->setWaitsForId(0);
555 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
556 checkHoldPosition(id, lat, lon, heading, speed, alt);
557 if (checkForCircularWaits(id)) {
558 i->setResolveCircularWait();
560 bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest();
561 int state = current->getState();
562 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
563 if ((now - lastTransmission) > 15) {
566 if (needsTaxiClearance && available) {
567 transmit(&(*current), MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND);
568 current->getAircraft()->setTaxiClearanceRequest(false);
569 current->setState(3);
570 lastTransmission = now;
573 if ((state == 3) && available) {
574 transmit(&(*current), MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR);
575 current->setState(4);
576 lastTransmission = now;
579 if ((state == 4) && available) {
580 transmit(&(*current), MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND);
581 current->setState(0);
582 lastTransmission = now;
588 Scan for a speed adjustment change. Find the nearest aircraft that is in front
589 and adjust speed when we get too close. Only do this when current position and/or
590 intentions of the current aircraft match current taxiroute position of the proximate
591 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
592 instruction. See below for the hold position instruction.
594 Note that there currently still is one flaw in the logic that needs to be addressed.
595 There can be situations where one aircraft is in front of the current aircraft, on a separate
596 route, but really close after an intersection coming off the current route. This
597 aircraft is still close enough to block the current aircraft. This situation is currently
598 not addressed yet, but should be.
601 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
602 double lon, double heading,
603 double speed, double alt)
606 TrafficVectorIterator current, closest;
607 TrafficVectorIterator i = activeTraffic.begin();
608 bool otherReasonToSlowDown = false;
609 bool previousInstruction;
610 if (activeTraffic.size()) {
611 //while ((i->getId() != id) && (i != activeTraffic.end()))
612 while (i != activeTraffic.end()) {
613 if (i->getId() == id) {
621 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
622 SG_LOG(SG_GENERAL, SG_ALERT,
623 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
628 previousInstruction = current->getSpeedAdjustment();
629 double mindist = HUGE_VAL;
630 if (activeTraffic.size()) {
631 double course, dist, bearing, minbearing, az2;
632 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
633 //TrafficVector iterator closest;
635 for (TrafficVectorIterator i = activeTraffic.begin();
636 i != activeTraffic.end(); i++) {
641 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
644 SGGeodesy::inverse(curr, other, course, az2, dist);
645 bearing = fabs(heading - course);
647 bearing = 360 - bearing;
648 if ((dist < mindist) && (bearing < 60.0)) {
651 minbearing = bearing;
654 //Check traffic at the tower controller
655 if (towerController->hasActiveTraffic()) {
656 for (TrafficVectorIterator i =
657 towerController->getActiveTraffic().begin();
658 i != towerController->getActiveTraffic().end(); i++) {
659 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
660 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
663 SGGeodesy::inverse(curr, other, course, az2, dist);
664 bearing = fabs(heading - course);
666 bearing = 360 - bearing;
667 if ((dist < mindist) && (bearing < 60.0)) {
670 minbearing = bearing;
671 otherReasonToSlowDown = true;
675 // Finally, check UserPosition
676 double userLatitude = fgGetDouble("/position/latitude-deg");
677 double userLongitude = fgGetDouble("/position/longitude-deg");
678 SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
679 SGGeodesy::inverse(curr, user, course, az2, dist);
681 bearing = fabs(heading - course);
683 bearing = 360 - bearing;
684 if ((dist < mindist) && (bearing < 60.0)) {
687 minbearing = bearing;
688 otherReasonToSlowDown = true;
691 current->clearSpeedAdjustment();
693 if (current->checkPositionAndIntentions(*closest)
694 || otherReasonToSlowDown) {
695 double maxAllowableDistance =
696 (1.1 * current->getRadius()) +
697 (1.1 * closest->getRadius());
698 if (mindist < 2 * maxAllowableDistance) {
699 if (current->getId() == closest->getWaitsForId())
702 current->setWaitsForId(closest->getId());
703 if (closest->getId() != current->getId())
704 current->setSpeedAdjustment(closest->getSpeed() *
707 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
708 if (mindist < maxAllowableDistance) {
709 //double newSpeed = (maxAllowableDistance-mindist);
710 //current->setSpeedAdjustment(newSpeed);
711 //if (mindist < 0.5* maxAllowableDistance)
713 current->setSpeedAdjustment(0);
722 Check for "Hold position instruction".
723 The hold position should be issued under the following conditions:
724 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
725 2) For taxiing aircraft that use one taxiway in opposite directions
726 3) For crossing or merging taxiroutes.
729 void FGGroundNetwork::checkHoldPosition(int id, double lat,
730 double lon, double heading,
731 double speed, double alt)
733 TrafficVectorIterator current;
734 TrafficVectorIterator i = activeTraffic.begin();
735 if (activeTraffic.size()) {
736 //while ((i->getId() != id) && i != activeTraffic.end())
737 while (i != activeTraffic.end()) {
738 if (i->getId() == id) {
746 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
747 SG_LOG(SG_GENERAL, SG_ALERT,
748 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
751 bool origStatus = current->hasHoldPosition();
752 current->setHoldPosition(false);
753 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
755 for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
756 if (i->getId() != current->getId()) {
757 int node = current->crosses(this, *i);
759 FGTaxiNode *taxiNode = findNode(node);
761 // Determine whether it's save to continue or not.
762 // If we have a crossing route, there are two possibilities:
763 // 1) This is an interestion
764 // 2) This is oncoming two-way traffic, using the same taxiway.
765 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
767 SGGeod other(SGGeod::
768 fromDegM(i->getLongitude(), i->getLatitude(),
772 if (current->isOpposing(this, *i, node)) {
775 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
776 // issue a "Hold Position" as soon as we're close to the offending node
777 // For now, I'm doing this as long as the other aircraft doesn't
778 // have a hold instruction as soon as we're within a reasonable
779 // distance from the offending node.
780 // This may be a bit of a conservative estimate though, as it may
781 // be well possible that both aircraft can both continue to taxi
782 // without crashing into each other.
785 if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
788 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
792 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
797 SGGeodesy::distanceM(curr, taxiNode->getGeod());
798 if (!(i->hasHoldPosition())) {
800 if ((dist < 200) && //2.5*current->getRadius()) &&
801 (needsToWait) && (i->onRoute(this, *current)) &&
802 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
803 (!(current->getId() == i->getWaitsForId())))
804 //(!(i->getSpeedAdjustment()))) // &&
805 //(!(current->getSpeedAdjustment())))
808 current->setHoldPosition(true);
809 current->setWaitsForId(i->getId());
810 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
811 // << dist << " meters. Waiting for " << i->getCallSign();
813 //cerr <<" [opposing] " << endl;
815 // cerr << "[non-opposing] " << endl;
816 //if (i->hasSpeefAdjustment())
818 // cerr << " (which in turn waits for ) " << i->
820 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
826 bool currStatus = current->hasHoldPosition();
828 // Either a Hold Position or a resume taxi transmission has been issued
829 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
830 if ((now - lastTransmission) > 2) {
833 if ((origStatus != currStatus) && available) {
834 //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
835 if (currStatus == true) { // No has a hold short instruction
836 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR);
837 //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
838 current->setState(1);
840 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR);
841 //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
842 current->setState(2);
844 lastTransmission = now;
846 // Don't act on the changed instruction until the transmission is confirmed
847 // So set back to original status
848 current->setHoldPosition(origStatus);
849 //cerr << "Current state " << current->getState() << endl;
852 int state = current->getState();
853 if ((state == 1) && (available)) {
854 //cerr << "ACKNOWLEDGE HOLD" << endl;
855 transmit(&(*current), MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND);
856 current->setState(0);
857 current->setHoldPosition(true);
858 lastTransmission = now;
862 if ((state == 2) && (available)) {
863 //cerr << "ACKNOWLEDGE RESUME" << endl;
864 transmit(&(*current), MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND);
865 current->setState(0);
866 current->setHoldPosition(false);
867 lastTransmission = now;
873 * Check whether situations occur where the current aircraft is waiting for itself
874 * due to higher order interactions.
875 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
876 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
877 * through this list of waiting aircraft, we can check if we'd eventually end back
878 * at the current aircraft.
880 * Note that we should consider the situation where we are actually checking aircraft
881 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
882 * the looping aircraft. If we don't check for that, this function will get stuck into
886 bool FGGroundNetwork::checkForCircularWaits(int id)
888 //cerr << "Performing Wait check " << id << endl;
890 TrafficVectorIterator current, other;
891 TrafficVectorIterator i = activeTraffic.begin();
892 int trafficSize = activeTraffic.size();
894 while (i != activeTraffic.end()) {
895 if (i->getId() == id) {
903 if (i == activeTraffic.end() || (trafficSize == 0)) {
904 SG_LOG(SG_GENERAL, SG_ALERT,
905 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
909 target = current->getWaitsForId();
910 //bool printed = false; // Note that this variable is for debugging purposes only.
914 //cerr << "aircraft waits for user" << endl;
919 while ((target > 0) && (target != id) && counter++ < trafficSize) {
921 TrafficVectorIterator i = activeTraffic.begin();
923 //while ((i->getId() != id) && i != activeTraffic.end())
924 while (i != activeTraffic.end()) {
925 if (i->getId() == target) {
933 if (i == activeTraffic.end() || (trafficSize == 0)) {
934 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
935 // The target id is not found on the current network, which means it's at the tower
936 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
940 target = other->getWaitsForId();
942 // actually this trap isn't as impossible as it first seemed:
943 // the setWaitsForID(id) is set to current when the aircraft
944 // is waiting for the user controlled aircraft.
945 //if (current->getId() == other->getId()) {
946 // cerr << "Caught the impossible trap" << endl;
947 // cerr << "Current = " << current->getId() << endl;
948 // cerr << "Other = " << other ->getId() << endl;
949 // for (TrafficVectorIterator at = activeTraffic.begin();
950 // at != activeTraffic.end();
952 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
955 if (current->getId() == other->getId())
958 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
959 // << " (" << other->getId() << "); " << endl;;
969 // cerr << "[done] " << endl << endl;;
971 SG_LOG(SG_GENERAL, SG_WARN,
972 "Detected circular wait condition: Id = " << id <<
973 "target = " << target);
980 // Note that this function is probably obsolete...
981 bool FGGroundNetwork::hasInstruction(int id)
983 TrafficVectorIterator i = activeTraffic.begin();
984 // Search search if the current id has an entry
985 // This might be faster using a map instead of a vector, but let's start by taking a safe route
986 if (activeTraffic.size()) {
987 //while ((i->getId() != id) && i != activeTraffic.end()) {
988 while (i != activeTraffic.end()) {
989 if (i->getId() == id) {
995 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
996 SG_LOG(SG_GENERAL, SG_ALERT,
997 "AI error: checking ATC instruction for aircraft without traffic record");
999 return i->hasInstruction();
1004 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1006 TrafficVectorIterator i = activeTraffic.begin();
1007 // Search search if the current id has an entry
1008 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1009 if (activeTraffic.size()) {
1010 //while ((i->getId() != id) && i != activeTraffic.end()) {
1011 while (i != activeTraffic.end()) {
1012 if (i->getId() == id) {
1018 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1019 SG_LOG(SG_GENERAL, SG_ALERT,
1020 "AI error: requesting ATC instruction for aircraft without traffic record");
1022 return i->getInstruction();
1024 return FGATCInstruction();