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