]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/Schedule.cxx
Bugfixes and some finetuning:
[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(0);
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     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_GENERAL, 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_GENERAL, 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