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