]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/Schedule.cxx
A number of cosmetic and/or infrastructural changes.
[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();
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_INPUT, 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()
373 {
374   if (!flights.empty()) {
375     return;
376   }
377   SG_LOG(SG_GENERAL, SG_BULK, "Scheduling Flights for : " << modelPath << " " <<  registration << " " << homePort);
378   FGScheduledFlight *flight = NULL;
379   do {
380     flight = findAvailableFlight(currentDestination, flightIdentifier);
381     if (!flight) {
382       break;
383     }
384     
385     currentDestination = flight->getArrivalAirport()->getId();
386     if (!initialized) {
387         string departurePort = flight->getDepartureAirport()->getId();
388        //cerr << "Scheduled " << registration <<  " " << score << " for Flight " 
389        //     << flight-> getCallSign() << " from " << departurePort << " to " << currentDestination << endl;
390         if (fgGetString("/sim/presets/airport-id") == departurePort) {
391             hits++;
392         }
393         //runCount++;
394         initialized = true;
395     }
396   
397     time_t arr, dep;
398     dep = flight->getDepartureTime();
399     arr = flight->getArrivalTime();
400     string depT = asctime(gmtime(&dep));
401     string arrT = asctime(gmtime(&arr));
402
403     depT = depT.substr(0,24);
404     arrT = arrT.substr(0,24);
405     SG_LOG(SG_GENERAL, SG_BULK, "  Flight " << flight->getCallSign() << ":" 
406                              << "  "        << flight->getDepartureAirport()->getId() << ":"
407                              << "  "        << depT << ":"
408                              << " \""       << flight->getArrivalAirport()->getId() << "\"" << ":"
409                              << "  "        << arrT << ":");
410   
411     flights.push_back(flight);
412   } while (1); //while (currentDestination != homePort);
413   SG_LOG(SG_GENERAL, SG_BULK, " Done ");
414 }
415
416 bool FGAISchedule::next()
417 {
418   if (!flights.empty()) {
419     flights.front()->release();
420     flights.erase(flights.begin());
421   }
422   
423   FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
424   if (!flight) {
425     return false;
426   }
427   
428   currentDestination = flight->getArrivalAirport()->getId();
429 /*
430   time_t arr, dep;
431   dep = flight->getDepartureTime();
432   arr = flight->getArrivalTime();
433   string depT = asctime(gmtime(&dep));
434   string arrT = asctime(gmtime(&arr));
435
436   depT = depT.substr(0,24);
437   arrT = arrT.substr(0,24);
438   //cerr << "  " << flight->getCallSign() << ":" 
439   //     << "  " << flight->getDepartureAirport()->getId() << ":"
440   //     << "  " << depT << ":"
441   //     << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
442   //     << "  " << arrT << ":" << endl;
443 */
444    flights.push_back(flight);
445    return true;
446 }
447
448 FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDestination,
449                                                       const string &req)
450 {
451     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
452
453     FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
454     FGScheduledFlightVecIterator fltBegin, fltEnd;
455     fltBegin = tmgr->getFirstFlight(req);
456     fltEnd   = tmgr->getLastFlight(req);
457
458
459      //cerr << "Finding available flight " << endl;
460      // For Now:
461      // Traverse every registered flight
462      if (fltBegin == fltEnd) {
463           //cerr << "No Flights Scheduled for " << req << endl;
464      }
465      int counter = 0;
466      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
467           (*i)->adjustTime(now);
468            //sort(fltBegin, fltEnd, compareScheduledFlights);
469            //cerr << counter++ << endl;
470      }
471      std::sort(fltBegin, fltEnd, compareScheduledFlights);
472      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
473           //bool valid = true;
474           counter++;
475           if (!(*i)->isAvailable()) {
476                //cerr << (*i)->getCallSign() << "is no longer available" << endl;
477                continue;
478           }
479           if (!((*i)->getRequirement() == req)) {
480                continue;
481           }
482           if (!(((*i)->getArrivalAirport()) && ((*i)->getDepartureAirport()))) {
483               continue;
484           }
485           if (!(currentDestination.empty())) {
486               if (currentDestination != (*i)->getDepartureAirport()->getId()) {
487                    //cerr << (*i)->getCallSign() << "Doesn't match destination" << endl;
488                    //cerr << "Current Destination " << currentDestination << "Doesnt match flight's " <<
489                    //          (*i)->getArrivalAirport()->getId() << endl;
490                    continue;
491               }
492           }
493           if (flights.size()) {
494             time_t arrival = flights.back()->getArrivalTime();
495             if ((*i)->getDepartureTime() < arrival)
496                 continue;
497           }
498
499           // So, if we actually get here, we have a winner
500           //cerr << "found flight: " << req << " : " << currentDestination << " : " <<       
501           //         (*i)->getArrivalAirport()->getId() << endl;
502           (*i)->lock();
503           return (*i);
504      }
505      // matches req?
506      // if currentDestination has a value, does it match departure of next flight?
507      // is departure time later than planned arrival?
508      // is departure port valid?
509      // is arrival port valid?
510      //cerr << "Ack no flight found: " << endl;
511      return NULL;
512 }
513
514 double FGAISchedule::getSpeed()
515 {
516   FGScheduledFlightVecIterator i = flights.begin();
517  
518   FGAirport* dep = (*i)->getDepartureAirport(),
519    *arr = (*i)->getArrivalAirport();
520   double dist = SGGeodesy::distanceNm(dep->geod(), arr->geod());
521   double remainingTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
522
523   double speed = dist / (remainingTimeEnroute/3600.0);
524   SG_CLAMP_RANGE(speed, 300.0, 500.0);
525   return speed;
526 }
527
528 void FGAISchedule::setScore   () 
529
530     if (runCount) {
531         score = ((double) hits / (double) runCount);
532     } else {
533         if (homePort == fgGetString("/sim/presets/airport-id")) {
534             score = 0.1;
535         } else {
536             score = 0.0;
537         }
538     }
539     runCount++;
540 }
541
542 bool compareSchedules(FGAISchedule*a, FGAISchedule*b)
543
544   return (*a) < (*b); 
545
546
547 bool FGAISchedule::operator< (const FGAISchedule &other) const
548
549     //cerr << "Sorting " << registration << " and "  << other.registration << endl;
550     double currentScore = score       * (1.5 - lastRun);
551     double otherScore   = other.score * (1.5 - other.lastRun);
552     return currentScore > otherScore;
553 }
554