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/simple.hxx>
34 #include <Airports/dynamics.hxx>
36 #include <AIModel/AIAircraft.hxx>
37 #include <AIModel/AIFlightPlan.hxx>
39 #include "groundnetwork.hxx"
41 /***************************************************************************
43 **************************************************************************/
45 void FGTaxiSegment::setStart(FGTaxiNodeVector * nodes)
47 FGTaxiNodeVectorIterator i = nodes->begin();
48 while (i != nodes->end()) {
49 //cerr << "Scanning start node index" << (*i)->getIndex() << endl;
50 if ((*i)->getIndex() == startNode) {
51 start = (*i)->getAddress();
52 (*i)->addSegment(this);
57 SG_LOG(SG_GENERAL, SG_ALERT,
58 "Could not find start node " << startNode << endl);
61 void FGTaxiSegment::setEnd(FGTaxiNodeVector * nodes)
63 FGTaxiNodeVectorIterator i = nodes->begin();
64 while (i != nodes->end()) {
65 //cerr << "Scanning end node index" << (*i)->getIndex() << endl;
66 if ((*i)->getIndex() == endNode) {
67 end = (*i)->getAddress();
72 SG_LOG(SG_GENERAL, SG_ALERT,
73 "Could not find end node " << endNode << endl);
78 // There is probably a computationally cheaper way of
80 void FGTaxiSegment::setTrackDistance()
82 length = SGGeodesy::distanceM(start->getGeod(), end->getGeod());
86 void FGTaxiSegment::setCourseDiff(double crse)
88 headingDiff = fabs(course - crse);
90 if (headingDiff > 180)
91 headingDiff = fabs(headingDiff - 360);
95 /***************************************************************************
97 **************************************************************************/
98 bool FGTaxiRoute::next(int *nde)
100 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
101 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
102 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
103 //if (currNode != nodes.end())
104 // cerr << "true" << endl;
106 // cerr << "false" << endl;
107 //if (nodes.size() != (routes.size()) +1)
108 // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
110 if (currNode == nodes.end())
113 if (currNode != nodes.begin()) // make sure route corresponds to the end node
119 bool FGTaxiRoute::next(int *nde, int *rte)
121 //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
122 // cerr << "FGTaxiRoute contains : " << *(i) << endl;
123 //cerr << "Offset from end: " << nodes.end() - currNode << endl;
124 //if (currNode != nodes.end())
125 // cerr << "true" << endl;
127 // cerr << "false" << endl;
128 if (nodes.size() != (routes.size()) + 1) {
129 SG_LOG(SG_GENERAL, SG_ALERT,
130 "ALERT: Misconfigured TaxiRoute : " << nodes.
131 size() << " " << routes.size());
134 if (currNode == nodes.end())
137 //*rte = *(currRoute);
138 if (currNode != nodes.begin()) // Make sure route corresponds to the end node
143 // If currNode points to the first node, this means the aircraft is not on the taxi node
144 // yet. Make sure to return a unique identifyer in this situation though, because otherwise
145 // the speed adjust AI code may be unable to resolve whether two aircraft are on the same
146 // taxi route or not. the negative of the preceding route seems a logical choice, as it is
147 // unique for any starting location.
148 // Note that this is probably just a temporary fix until I get Parking / tower control working.
149 *rte = -1 * *(currRoute);
155 void FGTaxiRoute::rewind(int route)
161 if (!(next(&currPoint, &currRoute))) {
162 SG_LOG(SG_GENERAL, SG_ALERT,
163 "Error in rewinding TaxiRoute: current" << currRoute <<
166 } while (currRoute != route);
172 /***************************************************************************
174 **************************************************************************/
175 bool compare_nodes(FGTaxiNode * a, FGTaxiNode * b)
180 bool compare_segments(FGTaxiSegment * a, FGTaxiSegment * b)
185 FGGroundNetwork::FGGroundNetwork()
193 currTraffic = activeTraffic.begin();
197 FGGroundNetwork::~FGGroundNetwork()
199 for (FGTaxiNodeVectorIterator node = nodes.begin();
200 node != nodes.end(); node++) {
204 pushBackNodes.clear();
205 for (FGTaxiSegmentVectorIterator seg = segments.begin();
206 seg != segments.end(); seg++) {
212 void FGGroundNetwork::addSegment(const FGTaxiSegment & seg)
214 segments.push_back(new FGTaxiSegment(seg));
217 void FGGroundNetwork::addNode(const FGTaxiNode & node)
219 nodes.push_back(new FGTaxiNode(node));
222 void FGGroundNetwork::addNodes(FGParkingVec * parkings)
225 FGParkingVecIterator i = parkings->begin();
226 while (i != parkings->end()) {
227 n.setIndex(i->getIndex());
228 n.setLatitude(i->getLatitude());
229 n.setLongitude(i->getLongitude());
230 nodes.push_back(new FGTaxiNode(n));
238 void FGGroundNetwork::init()
242 sort(nodes.begin(), nodes.end(), compare_nodes);
243 //sort(segments.begin(), segments.end(), compare_segments());
244 FGTaxiSegmentVectorIterator i = segments.begin();
245 while (i != segments.end()) {
246 (*i)->setStart(&nodes);
247 (*i)->setEnd(&nodes);
248 (*i)->setTrackDistance();
249 (*i)->setIndex(index);
250 if ((*i)->isPushBack()) {
251 pushBackNodes.push_back((*i)->getEnd());
253 //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl);
254 //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl);
255 //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to "
256 // << (*i)->getEnd()->getIndex() << endl);
261 i = segments.begin();
262 while (i != segments.end()) {
263 FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute();
264 while (j != (*i)->getEnd()->getEndRoute()) {
265 if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex()) {
266 // int start1 = (*i)->getStart()->getIndex();
267 // int end1 = (*i)->getEnd() ->getIndex();
268 // int start2 = (*j)->getStart()->getIndex();
269 // int end2 = (*j)->getEnd()->getIndex();
270 // int oppIndex = (*j)->getIndex();
271 //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") "
272 // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl;
273 (*i)->setOpposite(*j);
280 //FGTaxiNodeVectorIterator j = nodes.begin();
281 //while (j != nodes.end()) {
282 // if ((*j)->getHoldPointType() == 3) {
283 // pushBackNodes.push_back((*j));
287 //cerr << "Done initializing ground network" << endl;
291 int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
293 double minDist = HUGE_VAL;
296 for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end();
298 double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod());
301 index = (*itr)->getIndex();
302 //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
309 int FGGroundNetwork::findNearestNode(double lat, double lon)
311 return findNearestNode(SGGeod::fromDeg(lon, lat));
314 FGTaxiNode *FGGroundNetwork::findNode(unsigned idx)
316 for (FGTaxiNodeVectorIterator
318 itr != nodes.end(); itr++)
320 if (itr->getIndex() == idx)
321 return itr->getAddress();
324 if ((idx >= 0) && (idx < nodes.size()))
325 return nodes[idx]->getAddress();
330 FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
332 for (FGTaxiSegmentVectorIterator
333 itr = segments.begin();
334 itr != segments.end(); itr++)
336 if (itr->getIndex() == idx)
337 return itr->getAddress();
340 if ((idx > 0) && (idx <= segments.size()))
341 return segments[idx - 1]->getAddress();
343 //cerr << "Alert: trying to find invalid segment " << idx << endl;
349 FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
352 //implements Dijkstra's algorithm to find shortest distance route from start to end
353 //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
355 //double INFINITE = 100000000000.0;
356 // initialize scoring values
357 int nParkings = parent->getDynamics()->getNrOfParkings();
358 FGTaxiNodeVector *currNodesSet;
360 currNodesSet = &nodes;
362 currNodesSet = &pushBackNodes;
365 for (FGTaxiNodeVectorIterator
366 itr = currNodesSet->begin(); itr != currNodesSet->end(); itr++) {
367 (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means
368 (*itr)->setPreviousNode(0); //
369 (*itr)->setPreviousSeg(0); //
372 FGTaxiNode *firstNode = findNode(start);
373 firstNode->setPathScore(0);
375 FGTaxiNode *lastNode = findNode(end);
377 FGTaxiNodeVector unvisited(*currNodesSet); // working copy
379 while (!unvisited.empty()) {
380 FGTaxiNode *best = *(unvisited.begin());
381 for (FGTaxiNodeVectorIterator
382 itr = unvisited.begin(); itr != unvisited.end(); itr++) {
383 if ((*itr)->getPathScore() < best->getPathScore())
387 FGTaxiNodeVectorIterator newend =
388 remove(unvisited.begin(), unvisited.end(), best);
389 unvisited.erase(newend, unvisited.end());
391 if (best == lastNode) { // found route or best not connected
394 for (FGTaxiSegmentVectorIterator
395 seg = best->getBeginRoute();
396 seg != best->getEndRoute(); seg++) {
397 if (fullSearch || (*seg)->isPushBack()) {
398 FGTaxiNode *tgt = (*seg)->getEnd();
400 best->getPathScore() + (*seg)->getLength() +
401 (*seg)->getPenalty(nParkings);
402 if (alt < tgt->getPathScore()) { // Relax (u,v)
403 tgt->setPathScore(alt);
404 tgt->setPreviousNode(best);
405 tgt->setPreviousSeg(*seg); //
408 // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl;
414 if (lastNode->getPathScore() == HUGE_VAL) {
415 // no valid route found
417 SG_LOG(SG_GENERAL, SG_ALERT,
418 "Failed to find route from waypoint " << start << " to "
419 << end << " at " << parent->getId());
423 //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
425 // assemble route from backtrace information
426 intVec nodes, routes;
427 FGTaxiNode *bt = lastNode;
428 while (bt->getPreviousNode() != 0) {
429 nodes.push_back(bt->getIndex());
430 routes.push_back(bt->getPreviousSegment()->getIndex());
431 bt = bt->getPreviousNode();
433 nodes.push_back(start);
434 reverse(nodes.begin(), nodes.end());
435 reverse(routes.begin(), routes.end());
437 return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
441 int FGTaxiSegment::getPenalty(int nGates)
444 if (end->getIndex() < nGates) {
447 if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
453 /* ATC Related Functions */
455 void FGGroundNetwork::announcePosition(int id,
456 FGAIFlightPlan * intendedRoute,
457 int currentPosition, double lat,
458 double lon, double heading,
459 double speed, double alt,
460 double radius, int leg,
461 FGAIAircraft * aircraft)
464 TrafficVectorIterator i = activeTraffic.begin();
465 // Search search if the current id alread has an entry
466 // This might be faster using a map instead of a vector, but let's start by taking a safe route
467 if (activeTraffic.size()) {
468 //while ((i->getId() != id) && i != activeTraffic.end()) {
469 while (i != activeTraffic.end()) {
470 if (i->getId() == id) {
476 // Add a new TrafficRecord if no one exsists for this aircraft.
477 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
481 rec.setPositionAndIntentions(currentPosition, intendedRoute);
482 rec.setPositionAndHeading(lat, lon, heading, speed, alt);
483 rec.setRadius(radius); // only need to do this when creating the record.
484 rec.setAircraft(aircraft);
485 activeTraffic.push_back(rec);
487 i->setPositionAndIntentions(currentPosition, intendedRoute);
488 i->setPositionAndHeading(lat, lon, heading, speed, alt);
492 void FGGroundNetwork::signOff(int id)
494 TrafficVectorIterator i = activeTraffic.begin();
495 // Search search if the current id alread has an entry
496 // This might be faster using a map instead of a vector, but let's start by taking a safe route
497 if (activeTraffic.size()) {
498 //while ((i->getId() != id) && i != activeTraffic.end()) {
499 while (i != activeTraffic.end()) {
500 if (i->getId() == id) {
506 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
507 SG_LOG(SG_GENERAL, SG_ALERT,
508 "AI error: Aircraft without traffic record is signing off");
510 i = activeTraffic.erase(i);
514 void FGGroundNetwork::updateAircraftInformation(int id, double lat, double lon,
515 double heading, double speed, double alt,
518 // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to
519 // Transmit air-to-ground "Ready to taxi request:
520 // Transmit ground to air approval / hold
521 // Transmit confirmation ...
522 // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
525 TrafficVectorIterator i = activeTraffic.begin();
526 // Search search if the current id has an entry
527 // This might be faster using a map instead of a vector, but let's start by taking a safe route
528 TrafficVectorIterator current, closest;
529 if (activeTraffic.size()) {
530 //while ((i->getId() != id) && i != activeTraffic.end()) {
531 while (i != activeTraffic.end()) {
532 if (i->getId() == id) {
538 // update position of the current aircraft
539 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
540 SG_LOG(SG_GENERAL, SG_ALERT,
541 "AI error: updating aircraft without traffic record");
543 i->setPositionAndHeading(lat, lon, heading, speed, alt);
549 // Update every three secs, but add some randomness
550 // to prevent all IA objects doing this in synchrony
551 //if (getDt() < (3.0) + (rand() % 10))
555 current->clearResolveCircularWait();
556 current->setWaitsForId(0);
557 checkSpeedAdjustment(id, lat, lon, heading, speed, alt);
558 bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest();
559 if (!needsTaxiClearance) {
560 checkHoldPosition(id, lat, lon, heading, speed, alt);
561 if (checkForCircularWaits(id)) {
562 i->setResolveCircularWait();
565 current->setHoldPosition(true);
566 int state = current->getState();
567 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
568 if ((now - lastTransmission) > 15) {
571 if ((state < 3) && available) {
572 transmit(&(*current), MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND, true);
573 current->setState(3);
574 lastTransmission = now;
577 if ((state == 3) && available) {
578 transmit(&(*current), MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR, true);
579 current->setState(4);
580 lastTransmission = now;
583 if ((state == 4) && available) {
584 transmit(&(*current), MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND, true);
585 current->setState(5);
586 lastTransmission = now;
589 if ((state == 5) && available) {
590 current->setState(0);
591 current->getAircraft()->setTaxiClearanceRequest(false);
592 current->setHoldPosition(true);
601 Scan for a speed adjustment change. Find the nearest aircraft that is in front
602 and adjust speed when we get too close. Only do this when current position and/or
603 intentions of the current aircraft match current taxiroute position of the proximate
604 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
605 instruction. See below for the hold position instruction.
607 Note that there currently still is one flaw in the logic that needs to be addressed.
608 There can be situations where one aircraft is in front of the current aircraft, on a separate
609 route, but really close after an intersection coming off the current route. This
610 aircraft is still close enough to block the current aircraft. This situation is currently
611 not addressed yet, but should be.
614 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
615 double lon, double heading,
616 double speed, double alt)
619 TrafficVectorIterator current, closest;
620 TrafficVectorIterator i = activeTraffic.begin();
621 bool otherReasonToSlowDown = false;
622 bool previousInstruction;
623 if (activeTraffic.size()) {
624 //while ((i->getId() != id) && (i != activeTraffic.end()))
625 while (i != activeTraffic.end()) {
626 if (i->getId() == id) {
634 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
635 SG_LOG(SG_GENERAL, SG_ALERT,
636 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
641 previousInstruction = current->getSpeedAdjustment();
642 double mindist = HUGE_VAL;
643 if (activeTraffic.size()) {
644 double course, dist, bearing, minbearing, az2;
645 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
646 //TrafficVector iterator closest;
648 for (TrafficVectorIterator i = activeTraffic.begin();
649 i != activeTraffic.end(); i++) {
654 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
657 SGGeodesy::inverse(curr, other, course, az2, dist);
658 bearing = fabs(heading - course);
660 bearing = 360 - bearing;
661 if ((dist < mindist) && (bearing < 60.0)) {
664 minbearing = bearing;
667 //Check traffic at the tower controller
668 if (towerController->hasActiveTraffic()) {
669 for (TrafficVectorIterator i =
670 towerController->getActiveTraffic().begin();
671 i != towerController->getActiveTraffic().end(); i++) {
672 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
673 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
676 SGGeodesy::inverse(curr, other, course, az2, dist);
677 bearing = fabs(heading - course);
679 bearing = 360 - bearing;
680 if ((dist < mindist) && (bearing < 60.0)) {
683 minbearing = bearing;
684 otherReasonToSlowDown = true;
688 // Finally, check UserPosition
689 double userLatitude = fgGetDouble("/position/latitude-deg");
690 double userLongitude = fgGetDouble("/position/longitude-deg");
691 SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
692 SGGeodesy::inverse(curr, user, course, az2, dist);
694 bearing = fabs(heading - course);
696 bearing = 360 - bearing;
697 if ((dist < mindist) && (bearing < 60.0)) {
700 minbearing = bearing;
701 otherReasonToSlowDown = true;
704 current->clearSpeedAdjustment();
706 if (current->checkPositionAndIntentions(*closest)
707 || otherReasonToSlowDown) {
708 double maxAllowableDistance =
709 (1.1 * current->getRadius()) +
710 (1.1 * closest->getRadius());
711 if (mindist < 2 * maxAllowableDistance) {
712 if (current->getId() == closest->getWaitsForId())
715 current->setWaitsForId(closest->getId());
716 if (closest->getId() != current->getId())
717 current->setSpeedAdjustment(closest->getSpeed() *
720 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
721 if (mindist < maxAllowableDistance) {
722 //double newSpeed = (maxAllowableDistance-mindist);
723 //current->setSpeedAdjustment(newSpeed);
724 //if (mindist < 0.5* maxAllowableDistance)
726 current->setSpeedAdjustment(0);
735 Check for "Hold position instruction".
736 The hold position should be issued under the following conditions:
737 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it
738 2) For taxiing aircraft that use one taxiway in opposite directions
739 3) For crossing or merging taxiroutes.
742 void FGGroundNetwork::checkHoldPosition(int id, double lat,
743 double lon, double heading,
744 double speed, double alt)
746 TrafficVectorIterator current;
747 TrafficVectorIterator i = activeTraffic.begin();
748 if (activeTraffic.size()) {
749 //while ((i->getId() != id) && i != activeTraffic.end())
750 while (i != activeTraffic.end()) {
751 if (i->getId() == id) {
759 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
760 SG_LOG(SG_GENERAL, SG_ALERT,
761 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
764 bool origStatus = current->hasHoldPosition();
765 current->setHoldPosition(false);
766 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
768 for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
769 if (i->getId() != current->getId()) {
770 int node = current->crosses(this, *i);
772 FGTaxiNode *taxiNode = findNode(node);
774 // Determine whether it's save to continue or not.
775 // If we have a crossing route, there are two possibilities:
776 // 1) This is an interestion
777 // 2) This is oncoming two-way traffic, using the same taxiway.
778 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
780 SGGeod other(SGGeod::
781 fromDegM(i->getLongitude(), i->getLatitude(),
785 if (current->isOpposing(this, *i, node)) {
788 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
789 // issue a "Hold Position" as soon as we're close to the offending node
790 // For now, I'm doing this as long as the other aircraft doesn't
791 // have a hold instruction as soon as we're within a reasonable
792 // distance from the offending node.
793 // This may be a bit of a conservative estimate though, as it may
794 // be well possible that both aircraft can both continue to taxi
795 // without crashing into each other.
798 if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
801 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
805 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
810 SGGeodesy::distanceM(curr, taxiNode->getGeod());
811 if (!(i->hasHoldPosition())) {
813 if ((dist < 200) && //2.5*current->getRadius()) &&
814 (needsToWait) && (i->onRoute(this, *current)) &&
815 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
816 (!(current->getId() == i->getWaitsForId())))
817 //(!(i->getSpeedAdjustment()))) // &&
818 //(!(current->getSpeedAdjustment())))
821 current->setHoldPosition(true);
822 current->setWaitsForId(i->getId());
823 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
824 // << dist << " meters. Waiting for " << i->getCallSign();
826 //cerr <<" [opposing] " << endl;
828 // cerr << "[non-opposing] " << endl;
829 //if (i->hasSpeefAdjustment())
831 // cerr << " (which in turn waits for ) " << i->
833 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
839 bool currStatus = current->hasHoldPosition();
841 // Either a Hold Position or a resume taxi transmission has been issued
842 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
843 if ((now - lastTransmission) > 2) {
846 if ((origStatus != currStatus) && available) {
847 //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
848 if (currStatus == true) { // No has a hold short instruction
849 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true);
850 //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
851 current->setState(1);
853 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true);
854 //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
855 current->setState(2);
857 lastTransmission = now;
859 // Don't act on the changed instruction until the transmission is confirmed
860 // So set back to original status
861 current->setHoldPosition(origStatus);
862 //cerr << "Current state " << current->getState() << endl;
865 int state = current->getState();
866 if ((state == 1) && (available)) {
867 //cerr << "ACKNOWLEDGE HOLD" << endl;
868 transmit(&(*current), MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND, true);
869 current->setState(0);
870 current->setHoldPosition(true);
871 lastTransmission = now;
875 if ((state == 2) && (available)) {
876 //cerr << "ACKNOWLEDGE RESUME" << endl;
877 transmit(&(*current), MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND, true);
878 current->setState(0);
879 current->setHoldPosition(false);
880 lastTransmission = now;
886 * Check whether situations occur where the current aircraft is waiting for itself
887 * due to higher order interactions.
888 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
889 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
890 * through this list of waiting aircraft, we can check if we'd eventually end back
891 * at the current aircraft.
893 * Note that we should consider the situation where we are actually checking aircraft
894 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
895 * the looping aircraft. If we don't check for that, this function will get stuck into
899 bool FGGroundNetwork::checkForCircularWaits(int id)
901 //cerr << "Performing Wait check " << id << endl;
903 TrafficVectorIterator current, other;
904 TrafficVectorIterator i = activeTraffic.begin();
905 int trafficSize = activeTraffic.size();
907 while (i != activeTraffic.end()) {
908 if (i->getId() == id) {
916 if (i == activeTraffic.end() || (trafficSize == 0)) {
917 SG_LOG(SG_GENERAL, SG_ALERT,
918 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
922 target = current->getWaitsForId();
923 //bool printed = false; // Note that this variable is for debugging purposes only.
927 //cerr << "aircraft waits for user" << endl;
932 while ((target > 0) && (target != id) && counter++ < trafficSize) {
934 TrafficVectorIterator i = activeTraffic.begin();
936 //while ((i->getId() != id) && i != activeTraffic.end())
937 while (i != activeTraffic.end()) {
938 if (i->getId() == target) {
946 if (i == activeTraffic.end() || (trafficSize == 0)) {
947 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
948 // The target id is not found on the current network, which means it's at the tower
949 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
953 target = other->getWaitsForId();
955 // actually this trap isn't as impossible as it first seemed:
956 // the setWaitsForID(id) is set to current when the aircraft
957 // is waiting for the user controlled aircraft.
958 //if (current->getId() == other->getId()) {
959 // cerr << "Caught the impossible trap" << endl;
960 // cerr << "Current = " << current->getId() << endl;
961 // cerr << "Other = " << other ->getId() << endl;
962 // for (TrafficVectorIterator at = activeTraffic.begin();
963 // at != activeTraffic.end();
965 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
968 if (current->getId() == other->getId())
971 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
972 // << " (" << other->getId() << "); " << endl;;
982 // cerr << "[done] " << endl << endl;;
984 SG_LOG(SG_GENERAL, SG_WARN,
985 "Detected circular wait condition: Id = " << id <<
986 "target = " << target);
993 // Note that this function is probably obsolete...
994 bool FGGroundNetwork::hasInstruction(int id)
996 TrafficVectorIterator i = activeTraffic.begin();
997 // Search search if the current id has an entry
998 // This might be faster using a map instead of a vector, but let's start by taking a safe route
999 if (activeTraffic.size()) {
1000 //while ((i->getId() != id) && i != activeTraffic.end()) {
1001 while (i != activeTraffic.end()) {
1002 if (i->getId() == id) {
1008 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1009 SG_LOG(SG_GENERAL, SG_ALERT,
1010 "AI error: checking ATC instruction for aircraft without traffic record");
1012 return i->hasInstruction();
1017 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1019 TrafficVectorIterator i = activeTraffic.begin();
1020 // Search search if the current id has an entry
1021 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1022 if (activeTraffic.size()) {
1023 //while ((i->getId() != id) && i != activeTraffic.end()) {
1024 while (i != activeTraffic.end()) {
1025 if (i->getId() == id) {
1031 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1032 SG_LOG(SG_GENERAL, SG_ALERT,
1033 "AI error: requesting ATC instruction for aircraft without traffic record");
1035 return i->getInstruction();
1037 return FGATCInstruction();