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 bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest();
557 if (!needsTaxiClearance) {
558 checkHoldPosition(id, lat, lon, heading, speed, alt);
559 if (checkForCircularWaits(id)) {
560 i->setResolveCircularWait();
563 current->setHoldPosition(true);
564 int state = current->getState();
565 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
566 if ((now - lastTransmission) > 15) {
569 if ((state < 3) && available) {
570 transmit(&(*current), MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND);
571 current->setState(3);
572 lastTransmission = now;
575 if ((state == 3) && available) {
576 transmit(&(*current), MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR);
577 current->setState(4);
578 lastTransmission = now;
581 if ((state == 4) && available) {
582 transmit(&(*current), MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND);
583 current->setState(5);
584 lastTransmission = now;
587 if ((state == 5) && available) {
588 current->setState(0);
589 current->getAircraft()->setTaxiClearanceRequest(false);
590 current->setHoldPosition(true);
599 Scan for a speed adjustment change. Find the nearest aircraft that is in front
600 and adjust speed when we get too close. Only do this when current position and/or
601 intentions of the current aircraft match current taxiroute position of the proximate
602 aircraft. For traffic that is on other routes we need to issue a "HOLD Position"
603 instruction. See below for the hold position instruction.
605 Note that there currently still is one flaw in the logic that needs to be addressed.
606 There can be situations where one aircraft is in front of the current aircraft, on a separate
607 route, but really close after an intersection coming off the current route. This
608 aircraft is still close enough to block the current aircraft. This situation is currently
609 not addressed yet, but should be.
612 void FGGroundNetwork::checkSpeedAdjustment(int id, double lat,
613 double lon, double heading,
614 double speed, double alt)
617 TrafficVectorIterator current, closest;
618 TrafficVectorIterator i = activeTraffic.begin();
619 bool otherReasonToSlowDown = false;
620 bool previousInstruction;
621 if (activeTraffic.size()) {
622 //while ((i->getId() != id) && (i != activeTraffic.end()))
623 while (i != activeTraffic.end()) {
624 if (i->getId() == id) {
632 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
633 SG_LOG(SG_GENERAL, SG_ALERT,
634 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment");
639 previousInstruction = current->getSpeedAdjustment();
640 double mindist = HUGE_VAL;
641 if (activeTraffic.size()) {
642 double course, dist, bearing, minbearing, az2;
643 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
644 //TrafficVector iterator closest;
646 for (TrafficVectorIterator i = activeTraffic.begin();
647 i != activeTraffic.end(); i++) {
652 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
655 SGGeodesy::inverse(curr, other, course, az2, dist);
656 bearing = fabs(heading - course);
658 bearing = 360 - bearing;
659 if ((dist < mindist) && (bearing < 60.0)) {
662 minbearing = bearing;
665 //Check traffic at the tower controller
666 if (towerController->hasActiveTraffic()) {
667 for (TrafficVectorIterator i =
668 towerController->getActiveTraffic().begin();
669 i != towerController->getActiveTraffic().end(); i++) {
670 //cerr << "Comparing " << current->getId() << " and " << i->getId() << endl;
671 SGGeod other(SGGeod::fromDegM(i->getLongitude(),
674 SGGeodesy::inverse(curr, other, course, az2, dist);
675 bearing = fabs(heading - course);
677 bearing = 360 - bearing;
678 if ((dist < mindist) && (bearing < 60.0)) {
681 minbearing = bearing;
682 otherReasonToSlowDown = true;
686 // Finally, check UserPosition
687 double userLatitude = fgGetDouble("/position/latitude-deg");
688 double userLongitude = fgGetDouble("/position/longitude-deg");
689 SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude));
690 SGGeodesy::inverse(curr, user, course, az2, dist);
692 bearing = fabs(heading - course);
694 bearing = 360 - bearing;
695 if ((dist < mindist) && (bearing < 60.0)) {
698 minbearing = bearing;
699 otherReasonToSlowDown = true;
702 current->clearSpeedAdjustment();
704 if (current->checkPositionAndIntentions(*closest)
705 || otherReasonToSlowDown) {
706 double maxAllowableDistance =
707 (1.1 * current->getRadius()) +
708 (1.1 * closest->getRadius());
709 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() *
718 current->setSpeedAdjustment(0); // This can only happen when the user aircraft is the one closest
719 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)
744 TrafficVectorIterator current;
745 TrafficVectorIterator i = activeTraffic.begin();
746 if (activeTraffic.size()) {
747 //while ((i->getId() != id) && i != activeTraffic.end())
748 while (i != activeTraffic.end()) {
749 if (i->getId() == id) {
757 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
758 SG_LOG(SG_GENERAL, SG_ALERT,
759 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition");
762 bool origStatus = current->hasHoldPosition();
763 current->setHoldPosition(false);
764 SGGeod curr(SGGeod::fromDegM(lon, lat, alt));
766 for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
767 if (i->getId() != current->getId()) {
768 int node = current->crosses(this, *i);
770 FGTaxiNode *taxiNode = findNode(node);
772 // Determine whether it's save to continue or not.
773 // If we have a crossing route, there are two possibilities:
774 // 1) This is an interestion
775 // 2) This is oncoming two-way traffic, using the same taxiway.
776 //cerr << "Hold check 1 : " << id << " has common node " << node << endl;
778 SGGeod other(SGGeod::
779 fromDegM(i->getLongitude(), i->getLatitude(),
783 if (current->isOpposing(this, *i, node)) {
786 //cerr << "Hold check 2 : " << node << " has opposing segment " << endl;
787 // issue a "Hold Position" as soon as we're close to the offending node
788 // For now, I'm doing this as long as the other aircraft doesn't
789 // have a hold instruction as soon as we're within a reasonable
790 // distance from the offending node.
791 // This may be a bit of a conservative estimate though, as it may
792 // be well possible that both aircraft can both continue to taxi
793 // without crashing into each other.
796 if (SGGeodesy::distanceM(other, taxiNode->getGeod()) > 200) // 2.0*i->getRadius())
799 //cerr << "Hold check 3 : " << id <<" Other aircraft approaching node is still far away. (" << dist << " nm). Can safely continue "
803 //cerr << "Hold check 4: " << id << " Would need to wait for other aircraft : distance = " << dist << " meters" << endl;
808 SGGeodesy::distanceM(curr, taxiNode->getGeod());
809 if (!(i->hasHoldPosition())) {
811 if ((dist < 200) && //2.5*current->getRadius()) &&
812 (needsToWait) && (i->onRoute(this, *current)) &&
813 //((i->onRoute(this, *current)) || ((!(i->getSpeedAdjustment())))) &&
814 (!(current->getId() == i->getWaitsForId())))
815 //(!(i->getSpeedAdjustment()))) // &&
816 //(!(current->getSpeedAdjustment())))
819 current->setHoldPosition(true);
820 current->setWaitsForId(i->getId());
821 //cerr << "Hold check 5: " << current->getCallSign() <<" Setting Hold Position: distance to node (" << node << ") "
822 // << dist << " meters. Waiting for " << i->getCallSign();
824 //cerr <<" [opposing] " << endl;
826 // cerr << "[non-opposing] " << endl;
827 //if (i->hasSpeefAdjustment())
829 // cerr << " (which in turn waits for ) " << i->
831 //cerr << "Hold check 6: " << id << " No need to hold yet: Distance to node : " << dist << " nm"<< endl;
837 bool currStatus = current->hasHoldPosition();
839 // Either a Hold Position or a resume taxi transmission has been issued
840 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
841 if ((now - lastTransmission) > 2) {
844 if ((origStatus != currStatus) && available) {
845 //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl;
846 if (currStatus == true) { // No has a hold short instruction
847 transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR);
848 //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl;
849 current->setState(1);
851 transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR);
852 //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl;
853 current->setState(2);
855 lastTransmission = now;
857 // Don't act on the changed instruction until the transmission is confirmed
858 // So set back to original status
859 current->setHoldPosition(origStatus);
860 //cerr << "Current state " << current->getState() << endl;
863 int state = current->getState();
864 if ((state == 1) && (available)) {
865 //cerr << "ACKNOWLEDGE HOLD" << endl;
866 transmit(&(*current), MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND);
867 current->setState(0);
868 current->setHoldPosition(true);
869 lastTransmission = now;
873 if ((state == 2) && (available)) {
874 //cerr << "ACKNOWLEDGE RESUME" << endl;
875 transmit(&(*current), MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND);
876 current->setState(0);
877 current->setHoldPosition(false);
878 lastTransmission = now;
884 * Check whether situations occur where the current aircraft is waiting for itself
885 * due to higher order interactions.
886 * A 'circular' wait is a situation where a waits for b, b waits for c, and c waits
887 * for a. Ideally each aircraft only waits for one other aircraft, so by tracing
888 * through this list of waiting aircraft, we can check if we'd eventually end back
889 * at the current aircraft.
891 * Note that we should consider the situation where we are actually checking aircraft
892 * d, which is waiting for aircraft a. d is not part of the loop, but is held back by
893 * the looping aircraft. If we don't check for that, this function will get stuck into
897 bool FGGroundNetwork::checkForCircularWaits(int id)
899 //cerr << "Performing Wait check " << id << endl;
901 TrafficVectorIterator current, other;
902 TrafficVectorIterator i = activeTraffic.begin();
903 int trafficSize = activeTraffic.size();
905 while (i != activeTraffic.end()) {
906 if (i->getId() == id) {
914 if (i == activeTraffic.end() || (trafficSize == 0)) {
915 SG_LOG(SG_GENERAL, SG_ALERT,
916 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
920 target = current->getWaitsForId();
921 //bool printed = false; // Note that this variable is for debugging purposes only.
925 //cerr << "aircraft waits for user" << endl;
930 while ((target > 0) && (target != id) && counter++ < trafficSize) {
932 TrafficVectorIterator i = activeTraffic.begin();
934 //while ((i->getId() != id) && i != activeTraffic.end())
935 while (i != activeTraffic.end()) {
936 if (i->getId() == target) {
944 if (i == activeTraffic.end() || (trafficSize == 0)) {
945 //cerr << "[Waiting for traffic at Runway: DONE] " << endl << endl;;
946 // The target id is not found on the current network, which means it's at the tower
947 //SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits");
951 target = other->getWaitsForId();
953 // actually this trap isn't as impossible as it first seemed:
954 // the setWaitsForID(id) is set to current when the aircraft
955 // is waiting for the user controlled aircraft.
956 //if (current->getId() == other->getId()) {
957 // cerr << "Caught the impossible trap" << endl;
958 // cerr << "Current = " << current->getId() << endl;
959 // cerr << "Other = " << other ->getId() << endl;
960 // for (TrafficVectorIterator at = activeTraffic.begin();
961 // at != activeTraffic.end();
963 // cerr << "currently active aircraft : " << at->getCallSign() << " with Id " << at->getId() << " waits for " << at->getWaitsForId() << endl;
966 if (current->getId() == other->getId())
969 //cerr << current->getCallSign() << " (" << current->getId() << ") " << " -> " << other->getCallSign()
970 // << " (" << other->getId() << "); " << endl;;
980 // cerr << "[done] " << endl << endl;;
982 SG_LOG(SG_GENERAL, SG_WARN,
983 "Detected circular wait condition: Id = " << id <<
984 "target = " << target);
991 // Note that this function is probably obsolete...
992 bool FGGroundNetwork::hasInstruction(int id)
994 TrafficVectorIterator i = activeTraffic.begin();
995 // Search search if the current id has an entry
996 // This might be faster using a map instead of a vector, but let's start by taking a safe route
997 if (activeTraffic.size()) {
998 //while ((i->getId() != id) && i != activeTraffic.end()) {
999 while (i != activeTraffic.end()) {
1000 if (i->getId() == id) {
1006 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1007 SG_LOG(SG_GENERAL, SG_ALERT,
1008 "AI error: checking ATC instruction for aircraft without traffic record");
1010 return i->hasInstruction();
1015 FGATCInstruction FGGroundNetwork::getInstruction(int id)
1017 TrafficVectorIterator i = activeTraffic.begin();
1018 // Search search if the current id has an entry
1019 // This might be faster using a map instead of a vector, but let's start by taking a safe route
1020 if (activeTraffic.size()) {
1021 //while ((i->getId() != id) && i != activeTraffic.end()) {
1022 while (i != activeTraffic.end()) {
1023 if (i->getId() == id) {
1029 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
1030 SG_LOG(SG_GENERAL, SG_ALERT,
1031 "AI error: requesting ATC instruction for aircraft without traffic record");
1033 return i->getInstruction();
1035 return FGATCInstruction();