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