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