]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/Schedule.cxx
Synchronized FG with the removal of 'using std::*' in simgear's easyxml
[flightgear.git] / src / Traffic / Schedule.cxx
1 /******************************************************************************
2  * Schedule.cxx
3  * Written by Durk Talsma, started May 5, 2004.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  *
19  *
20  ****************************************************************************
21  *
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #define BOGUS 0xFFFF
29
30 #include <stdlib.h>
31 #include <time.h>
32 #include <iostream>
33 #include <fstream>
34
35
36 #include <string>
37 #include <vector>
38 #include <algorithm>
39 #include <boost/foreach.hpp>
40
41 #include <simgear/compiler.h>
42 #include <simgear/sg_inlines.h>
43 #include <simgear/math/sg_geodesy.hxx>
44 #include <simgear/props/props.hxx>
45 #include <simgear/structure/subsystem_mgr.hxx>
46 #include <simgear/xml/easyxml.hxx>
47
48 #include <AIModel/AIFlightPlan.hxx>
49 #include <AIModel/AIManager.hxx>
50 #include <AIModel/AIAircraft.hxx>
51 #include <Airports/airport.hxx>
52 #include <Main/fg_init.hxx>   // That's pretty ugly, but I need fgFindAirportID
53
54
55 #include "SchedFlight.hxx"
56 #include "TrafficMgr.hxx"
57
58 using std::string;
59
60 /******************************************************************************
61  * the FGAISchedule class contains data members and code to maintain a
62  * schedule of Flights for an artificially controlled aircraft.
63  *****************************************************************************/
64 FGAISchedule::FGAISchedule()
65   : heavy(false),
66     radius(0),
67     groundOffset(0),
68     distanceToUser(0),
69     score(0),
70     runCount(0),
71     hits(0),
72     lastRun(0),
73     firstRun(false),
74     courseToDest(0),
75     initialized(false),
76     valid(false),
77     scheduleComplete(false)
78 {
79 }
80
81
82 FGAISchedule::FGAISchedule(const string& model,
83                            const string& lvry,
84                            const string& port,
85                            const string& reg,
86                            const string& flightId,
87                            bool   hvy, 
88                            const string& act,
89                            const string& arln,
90                            const string& mclass,
91                            const string& fltpe,
92                            double rad, 
93                            double grnd)
94     : heavy(hvy),
95       radius(rad),
96       groundOffset(grnd),
97       distanceToUser(0),
98       score(0),
99       runCount(0),
100       hits(0),
101       lastRun(0),
102       firstRun(true),
103       courseToDest(0),
104       initialized(false),
105       valid(true),
106       scheduleComplete(false)
107 {
108   modelPath        = model; 
109   livery           = lvry; 
110   homePort         = port;
111   registration     = reg;
112   flightIdentifier = flightId;
113   acType           = act;
114   airline          = arln;
115   m_class          = mclass;
116   flightType       = fltpe;
117   /*for (FGScheduledFlightVecIterator i = flt.begin();
118        i != flt.end();
119        i++)
120     flights.push_back(new FGScheduledFlight((*(*i))));*/
121 }
122
123 FGAISchedule::FGAISchedule(const FGAISchedule &other)
124 {
125   modelPath          = other.modelPath;
126   homePort           = other.homePort;
127   livery             = other.livery;
128   registration       = other.registration;
129   heavy              = other.heavy;
130   flightIdentifier   = other.flightIdentifier;
131   flights            = other.flights;
132   aiAircraft         = other.aiAircraft;
133   acType             = other.acType;
134   airline            = other.airline;
135   m_class            = other.m_class;
136   firstRun           = other.firstRun;
137   radius             = other.radius;
138   groundOffset       = other.groundOffset;
139   flightType         = other.flightType;
140   score              = other.score;
141   distanceToUser     = other.distanceToUser;
142   currentDestination = other.currentDestination;
143   firstRun           = other.firstRun;
144   runCount           = other.runCount;
145   hits               = other.hits;
146   lastRun            = other.lastRun;
147   courseToDest       = other.courseToDest;
148   initialized        = other.initialized;
149   valid              = other.valid;
150   scheduleComplete   = other.scheduleComplete;
151 }
152
153
154
155 FGAISchedule::~FGAISchedule()
156 {
157     // remove related object from AI manager
158     if (aiAircraft)
159     {
160       aiAircraft->setDie(true);
161     }
162
163 /*  for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++)
164     {
165       delete (*flt);
166     }
167   flights.clear();*/
168
169
170 bool FGAISchedule::init()
171 {
172   //tm targetTimeDate;
173   //SGTime* currTimeDate = globals->get_time_params();
174
175   //tm *temp = currTimeDate->getGmt();
176   //char buffer[512];
177   //sgTimeFormatTime(&targetTimeDate, buffer);
178   //cout << "Scheduled Time " << buffer << endl; 
179   //cout << "Time :" << time(NULL) << " SGTime : " << sgTimeGetGMT(temp) << endl;
180   /*for (FGScheduledFlightVecIterator i = flights.begin(); 
181        i != flights.end(); 
182        i++)
183     {
184       //i->adjustTime(now);
185       if (!((*i)->initializeAirports()))
186         return false;
187     } */
188   //sort(flights.begin(), flights.end());
189   // Since time isn't initialized yet when this function is called,
190   // Find the closest possible airport.
191   // This should give a reasonable initialization order. 
192   //setClosestDistanceToUser();
193   return true;
194 }
195
196 /**
197  *  Returns true when processing is complete.
198  *  Returns false when processing was aborted due to timeout, so
199  *    more time required - and another call is requested (next sim iteration).
200  */
201 bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
202 {
203
204   time_t totalTimeEnroute,
205          elapsedTimeEnroute,
206          //remainingTimeEnroute,
207          deptime = 0;
208
209   if (!valid) {
210     return true; // processing complete
211   }
212
213   if (!scheduleComplete) {
214       scheduleComplete = scheduleFlights(now);
215   }
216
217   if (!scheduleComplete) {
218       return false; // not ready yet, continue processing in next iteration
219   }
220
221   if (flights.empty()) { // No flights available for this aircraft
222       valid = false;
223       return true; // processing complete
224   }
225
226
227   // Sort all the scheduled flights according to scheduled departure time.
228   // Because this is done at every update, we only need to check the status
229   // of the first listed flight. 
230   //sort(flights.begin(), flights.end(), compareScheduledFlights);
231   
232   if (firstRun) {
233      if (fgGetBool("/sim/traffic-manager/instantaneous-action") == true) {
234          deptime = now; // + rand() % 300; // Wait up to 5 minutes until traffic starts moving to prevent too many aircraft 
235                                    // from cluttering the gate areas.
236      }
237      firstRun = false;
238   }
239   
240   FGScheduledFlight* flight = flights.front();
241   if (!deptime) {
242     deptime = flight->getDepartureTime();
243     //cerr << "Setting departure time " << deptime << endl;
244   }
245     
246   if (aiAircraft) {
247     if (aiAircraft->getDie()) {
248       aiAircraft = NULL;
249     } else {
250       return true; // in visual range, let the AIManager handle it
251     }
252   }
253   
254   // This flight entry is entirely in the past, do we need to 
255   // push it forward in time to the next scheduled departure. 
256   if (flight->getArrivalTime() < now) {
257     SG_LOG (SG_AI, SG_BULK, "Traffic Manager:      Flight is in the Past");
258     // Don't just update: check whether we need to load a new leg. etc.
259     // This update occurs for distant aircraft, so we can update the current leg
260     // and detach it from the current list of aircraft. 
261     flight->update();
262     flights.erase(flights.begin()); // pop_front(), effectively
263     return true; // processing complete
264   }
265   
266   FGAirport* dep = flight->getDepartureAirport();
267   FGAirport* arr = flight->getArrivalAirport();
268   if (!dep || !arr) {
269     return true; // processing complete
270   }
271     
272   double speed = 450.0;
273   if (dep != arr) {
274     totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime();
275     if (flight->getDepartureTime() < now) {
276       elapsedTimeEnroute   = now - flight->getDepartureTime();
277       //remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute;
278       double x = elapsedTimeEnroute / (double) totalTimeEnroute;
279       
280     // current pos is based on great-circle course between departure/arrival,
281     // with percentage of distance travelled, based upon percentage of time
282     // enroute elapsed.
283       double course, az2, distanceM;
284       SGGeodesy::inverse(dep->geod(), arr->geod(), course, az2, distanceM);
285       double coveredDistance = distanceM * x;
286       
287       SGGeodesy::direct(dep->geod(), course, coveredDistance, position, az2);
288       
289       SG_LOG (SG_AI, SG_BULK, "Traffic Manager:      Flight is in progress, %=" << x);
290       speed = ((distanceM - coveredDistance) * SG_METER_TO_NM) / 3600.0;
291     } else {
292     // not departed yet
293       //remainingTimeEnroute = totalTimeEnroute;
294       elapsedTimeEnroute = 0;
295       position = dep->geod();
296       SG_LOG (SG_AI, SG_BULK, "Traffic Manager:      Flight is pending, departure in "
297         << flight->getDepartureTime() - now << " seconds ");
298     }
299   } else {
300     // departure / arrival coincident
301     //remainingTimeEnroute = totalTimeEnroute = 0.0;
302     elapsedTimeEnroute = 0;
303     position = dep->geod();
304   }
305     
306   // cartesian calculations are more numerically stable over the (potentially)
307   // large distances involved here: see bug #80
308   distanceToUser = dist(userCart, SGVec3d::fromGeod(position)) * SG_METER_TO_NM;
309
310
311   // If distance between user and simulated aircraft is less
312   // then 500nm, create this flight. At jet speeds 500 nm is roughly
313   // one hour flight time, so that would be a good approximate point
314   // to start a more detailed simulation of this aircraft.
315   SG_LOG (SG_AI, SG_BULK, "Traffic manager: " << registration << " is scheduled for a flight from "
316              << dep->getId() << " to " << arr->getId() << ". Current distance to user: " 
317              << distanceToUser);
318   if (distanceToUser >= TRAFFICTOAIDISTTOSTART) {
319     return true; // out of visual range, for the moment.
320   }
321
322   if (!createAIAircraft(flight, speed, deptime)) {
323       valid = false;
324   }
325
326
327     return true; // processing complete
328 }
329
330 bool FGAISchedule::validModelPath(const std::string& modelPath)
331 {
332     return (resolveModelPath(modelPath) != SGPath());
333 }
334
335 SGPath FGAISchedule::resolveModelPath(const std::string& modelPath)
336 {
337     BOOST_FOREACH(SGPath aiPath, globals->get_data_paths("AI")) {
338         aiPath.append(modelPath);
339         if (aiPath.exists()) {
340             return aiPath;
341         }
342     }
343     
344     // check aircraft dirs
345     BOOST_FOREACH(std::string aircraftPath, globals->get_aircraft_paths()) {
346         SGPath mp(aircraftPath);
347         mp.append(modelPath);
348         if (mp.exists()) {
349             return mp;
350         }
351     }
352
353     return SGPath();
354 }
355
356 bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime)
357 {
358   FGAirport* dep = flight->getDepartureAirport();
359   FGAirport* arr = flight->getArrivalAirport();
360   string flightPlanName = dep->getId() + "-" + arr->getId() + ".xml";
361   SG_LOG(SG_AI, SG_DEBUG, "Traffic manager: Creating AIModel from:" << flightPlanName);
362
363   aiAircraft = new FGAIAircraft(this);
364   aiAircraft->setPerformance(acType, m_class); //"jet_transport";
365   aiAircraft->setCompany(airline); //i->getAirline();
366   aiAircraft->setAcType(acType); //i->getAcType();
367   aiAircraft->setPath(modelPath.c_str());
368   //aircraft->setFlightPlan(flightPlanName);
369   aiAircraft->setLatitude(position.getLatitudeDeg());
370   aiAircraft->setLongitude(position.getLongitudeDeg());
371   aiAircraft->setAltitude(flight->getCruiseAlt()*100); // convert from FL to feet
372   aiAircraft->setSpeed(0);
373   aiAircraft->setBank(0);
374       
375   courseToDest = SGGeodesy::courseDeg(position, arr->geod());
376   FGAIFlightPlan *fp = new FGAIFlightPlan(aiAircraft, flightPlanName, courseToDest, deptime,
377                                             dep, arr, true, radius, 
378                                             flight->getCruiseAlt()*100, 
379                                             position.getLatitudeDeg(), 
380                                             position.getLongitudeDeg(), 
381                                             speedKnots, flightType, acType, 
382                                             airline);
383   if (fp->isValidPlan()) {
384         aiAircraft->SetFlightPlan(fp);
385         FGAIManager* aimgr = (FGAIManager *) globals-> get_subsystem("ai-model");
386         aimgr->attach(aiAircraft);
387         return true;
388   } else {
389         aiAircraft = NULL;
390         delete fp;
391         //hand back the flights that had already been scheduled
392         while (!flights.empty()) {
393             flights.front()->release();
394             flights.erase(flights.begin());
395         }
396         return false;
397   }
398 }
399
400 // Create an initial heading for user controlled aircraft.
401 void FGAISchedule::setHeading()  
402
403     courseToDest = SGGeodesy::courseDeg((*flights.begin())->getDepartureAirport()->geod(), (*flights.begin())->getArrivalAirport()->geod());
404 }
405
406 bool FGAISchedule::scheduleFlights(time_t now)
407 {
408   //string startingPort;
409   const string& userPort = fgGetString("/sim/presets/airport-id");
410   SG_LOG(SG_AI, SG_BULK, "Scheduling Flights for : " << modelPath << " " <<  registration << " " << homePort);
411   FGScheduledFlight *flight = NULL;
412   SGTimeStamp start;
413   start.stamp();
414
415   bool first = true;
416   if (currentDestination.empty())
417     flight = findAvailableFlight(userPort, flightIdentifier, now, (now+6400));
418
419   do {
420     if ((!flight)||(!first)) {
421         flight = findAvailableFlight(currentDestination, flightIdentifier);
422     }
423     if (!flight) {
424       break;
425     }
426
427     first = false;
428     currentDestination = flight->getArrivalAirport()->getId();
429     //cerr << "Current destination " <<  currentDestination << endl;
430     if (!initialized) {
431         const string& departurePort = flight->getDepartureAirport()->getId();
432         if (userPort == departurePort) {
433             lastRun = 1;
434             hits++;
435         } else {
436             lastRun = 0;
437         }
438         //runCount++;
439         initialized = true;
440     }
441   
442     if (sglog().would_log(SG_AI, SG_BULK))
443     {
444         time_t arr, dep;
445         dep = flight->getDepartureTime();
446         arr = flight->getArrivalTime();
447         string depT = asctime(gmtime(&dep));
448         string arrT = asctime(gmtime(&arr));
449         depT = depT.substr(0,24);
450         arrT = arrT.substr(0,24);
451         SG_LOG(SG_AI, SG_BULK, "  Flight " << flight->getCallSign() << ":"
452                                  << "  "        << flight->getDepartureAirport()->getId() << ":"
453                                  << "  "        << depT << ":"
454                                  << " \""       << flight->getArrivalAirport()->getId() << "\"" << ":"
455                                  << "  "        << arrT << ":");
456     }
457   
458     flights.push_back(flight);
459
460     // continue processing until complete, or preempt after timeout
461   } while ((currentDestination != homePort)&&
462            (start.elapsedMSec()<3.0));
463
464   if (flight && (currentDestination != homePort))
465   {
466       // processing preempted, need to continue in next iteration
467       return false;
468   }
469
470   SG_LOG(SG_AI, SG_BULK, " Done ");
471   return true;
472 }
473
474 bool FGAISchedule::next()
475 {
476   if (!flights.empty()) {
477     flights.front()->release();
478     flights.erase(flights.begin());
479   }
480   
481   FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
482   if (!flight) {
483     return false;
484   }
485   
486   currentDestination = flight->getArrivalAirport()->getId();
487 /*
488   time_t arr, dep;
489   dep = flight->getDepartureTime();
490   arr = flight->getArrivalTime();
491   string depT = asctime(gmtime(&dep));
492   string arrT = asctime(gmtime(&arr));
493
494   depT = depT.substr(0,24);
495   arrT = arrT.substr(0,24);
496   //cerr << "  " << flight->getCallSign() << ":" 
497   //     << "  " << flight->getDepartureAirport()->getId() << ":"
498   //     << "  " << depT << ":"
499   //     << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
500   //     << "  " << arrT << ":" << endl;
501 */
502    flights.push_back(flight);
503    return true;
504 }
505
506 FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDestination,
507                                                       const string &req,
508                                                      time_t min, time_t max)
509 {
510     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
511
512     FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("traffic-manager");
513     FGScheduledFlightVecIterator fltBegin, fltEnd;
514     fltBegin = tmgr->getFirstFlight(req);
515     fltEnd   = tmgr->getLastFlight(req);
516
517
518      //cerr << "Finding available flight " << endl;
519      // For Now:
520      // Traverse every registered flight
521      if (fltBegin == fltEnd) {
522           //cerr << "No Flights Scheduled for " << req << endl;
523      }
524      int counter = 0;
525      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
526           (*i)->adjustTime(now);
527            //sort(fltBegin, fltEnd, compareScheduledFlights);
528            //cerr << counter++ << endl;
529      }
530      std::sort(fltBegin, fltEnd, compareScheduledFlights);
531      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
532           //bool valid = true;
533           counter++;
534           if (!(*i)->isAvailable()) {
535                //cerr << (*i)->getCallSign() << "is no longer available" << endl;
536                continue;
537           }
538           if (!((*i)->getRequirement() == req)) {
539                continue;
540           }
541           if (!(((*i)->getArrivalAirport()) && ((*i)->getDepartureAirport()))) {
542               continue;
543           }
544           if (!(currentDestination.empty())) {
545               if (currentDestination != (*i)->getDepartureAirport()->getId()) {
546                    //cerr << (*i)->getCallSign() << "Doesn't match destination" << endl;
547                    //cerr << "Current Destination " << currentDestination << "Doesnt match flight's " <<
548                    //          (*i)->getArrivalAirport()->getId() << endl;
549                    continue;
550               }
551           }
552           if (! flights.empty()) {
553             time_t arrival = flights.back()->getArrivalTime();
554             int groundTime = groundTimeFromRadius();
555             if ((*i)->getDepartureTime() < (arrival+(groundTime)))
556                 continue;
557           }
558           if (min != 0) {
559               time_t dep = (*i)->getDepartureTime();
560               if ((dep < min) || (dep > max))
561                   continue;
562           }
563
564           // So, if we actually get here, we have a winner
565           //cerr << "found flight: " << req << " : " << currentDestination << " : " <<       
566           //         (*i)->getArrivalAirport()->getId() << endl;
567           (*i)->lock();
568           return (*i);
569      }
570      // matches req?
571      // if currentDestination has a value, does it match departure of next flight?
572      // is departure time later than planned arrival?
573      // is departure port valid?
574      // is arrival port valid?
575      //cerr << "Ack no flight found: " << endl;
576      return NULL;
577 }
578
579 int FGAISchedule::groundTimeFromRadius()
580 {
581     if (radius < 10) 
582         return 15 * 60;
583     else if (radius < 15)
584         return 20 * 60;
585     else if (radius < 20)
586         return 30 * 60;
587     else if (radius < 25)
588         return 50 * 60;
589     else if (radius < 30)
590         return 90 * 60;
591     else 
592         return 120 * 60;
593 }
594
595
596 double FGAISchedule::getSpeed()
597 {
598   FGScheduledFlightVecIterator i = flights.begin();
599  
600   FGAirport* dep = (*i)->getDepartureAirport(),
601    *arr = (*i)->getArrivalAirport();
602   double dist = SGGeodesy::distanceNm(dep->geod(), arr->geod());
603   double remainingTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
604
605   double speed = dist / (remainingTimeEnroute/3600.0);
606   SG_CLAMP_RANGE(speed, 300.0, 500.0);
607   return speed;
608 }
609
610 void FGAISchedule::setScore   () 
611
612     if (runCount) {
613         score = ((double) hits / (double) runCount);
614     } else {
615         if (homePort == fgGetString("/sim/presets/airport-id")) {
616             score = 0.1;
617         } else {
618             score = 0.0;
619         }
620     }
621     runCount++;
622 }
623
624 bool compareSchedules(FGAISchedule*a, FGAISchedule*b)
625
626   return (*a) < (*b); 
627
628
629 bool FGAISchedule::operator< (const FGAISchedule &other) const
630
631     //cerr << "Sorting " << registration << " and "  << other.registration << endl;
632     double currentScore = score       * (1.5 - lastRun);
633     double otherScore   = other.score * (1.5 - other.lastRun);
634     return currentScore > otherScore;
635 }
636