]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/Schedule.cxx
e86cbcf243097940914a04d92e44afaa06a15377
[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   if (!fgGetBool("/sim/traffic-manager/enabled"))
191     return true;
192   
193   time_t 
194     totalTimeEnroute, 
195     elapsedTimeEnroute,
196     remainingTimeEnroute, 
197     deptime = 0;
198   
199   scheduleFlights();
200   if (flights.empty()) { // No flights available for this aircraft
201       return false;
202   }
203   
204   // Sort all the scheduled flights according to scheduled departure time.
205   // Because this is done at every update, we only need to check the status
206   // of the first listed flight. 
207   //sort(flights.begin(), flights.end(), compareScheduledFlights);
208   
209   if (firstRun) {
210      if (fgGetBool("/sim/traffic-manager/instantaneous-action") == true) {
211          deptime = now; // + rand() % 300; // Wait up to 5 minutes until traffic starts moving to prevent too many aircraft 
212                                    // from cluttering the gate areas.
213      }
214      firstRun = false;
215   }
216   
217   FGScheduledFlight* flight = flights.front();
218   if (!deptime) {
219     deptime = flight->getDepartureTime();
220     //cerr << "Settiing departure time " << deptime << endl;
221   }
222     
223   if (AIManagerRef) {
224     // Check if this aircraft has been released. 
225     FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
226     if (tmgr->isReleased(AIManagerRef)) {
227       AIManagerRef = NULL;
228     } else {
229       return true; // in visual range, let the AIManager handle it
230     }
231   }
232   
233   // This flight entry is entirely in the past, do we need to 
234   // push it forward in time to the next scheduled departure. 
235   if (flight->getArrivalTime() < now) {
236     SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager:      Flight is in the Past");
237     // Don't just update: check whether we need to load a new leg. etc.
238     // This update occurs for distant aircraft, so we can update the current leg
239     // and detach it from the current list of aircraft. 
240           flight->update();
241     flights.erase(flights.begin()); // pop_front(), effectively
242           return true;
243         }
244   
245   FGAirport* dep = flight->getDepartureAirport();
246   FGAirport* arr = flight->getArrivalAirport();
247   if (!dep || !arr) {
248     return false;
249   }
250     
251   double speed = 450.0;
252   if (dep != arr) {
253     totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime();
254     if (flight->getDepartureTime() < now) {
255       elapsedTimeEnroute   = now - flight->getDepartureTime();
256       remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute;
257       double x = elapsedTimeEnroute / (double) totalTimeEnroute;
258       
259     // current pos is based on great-circle course between departure/arrival,
260     // with percentage of distance travelled, based upon percentage of time
261     // enroute elapsed.
262       double course, az2, distanceM;
263       SGGeodesy::inverse(dep->geod(), arr->geod(), course, az2, distanceM);
264       double coveredDistance = distanceM * x;
265       
266       SGGeodesy::direct(dep->geod(), course, coveredDistance, position, az2);
267       
268       SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager:      Flight is in progress, %=" << x);
269       speed = ((distanceM - coveredDistance) * SG_METER_TO_NM) / 3600.0;
270     } else {
271     // not departed yet
272       remainingTimeEnroute = totalTimeEnroute;
273       elapsedTimeEnroute = 0;
274       position = dep->geod();
275       SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager:      Flight is pending, departure in "
276         << flight->getDepartureTime() - now << " seconds ");
277     }
278   } else {
279     // departure / arrival coincident
280     remainingTimeEnroute = totalTimeEnroute = 0.0;
281     elapsedTimeEnroute = 0;
282     position = dep->geod();
283   }
284     
285   // cartesian calculations are more numerically stable over the (potentially)
286   // large distances involved here: see bug #80
287   distanceToUser = dist(userCart, SGVec3d::fromGeod(position)) * SG_METER_TO_NM;
288
289   // If distance between user and simulated aircaft is less
290   // then 500nm, create this flight. At jet speeds 500 nm is roughly
291   // one hour flight time, so that would be a good approximate point
292   // to start a more detailed simulation of this aircraft.
293   SG_LOG (SG_GENERAL, SG_BULK, "Traffic manager: " << registration << " is scheduled for a flight from " 
294              << dep->getId() << " to " << arr->getId() << ". Current distance to user: " 
295              << distanceToUser);
296   if (distanceToUser >= TRAFFICTOAIDISTTOSTART) {
297     return true; // out of visual range, for the moment.
298   }
299   return createAIAircraft(flight, speed, deptime);
300 }
301
302 bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime)
303 {
304   FGAirport* dep = flight->getDepartureAirport();
305   FGAirport* arr = flight->getArrivalAirport();
306   string flightPlanName = dep->getId() + "-" + arr->getId() + ".xml";
307   SG_LOG(SG_GENERAL, SG_INFO, "Traffic manager: Creating AIModel from:" << flightPlanName);
308
309   // Only allow traffic to be created when the model path (or the AI version of mp) exists
310   SGPath mp(globals->get_fg_root());
311   SGPath mp_ai = mp;
312
313   mp.append(modelPath);
314   mp_ai.append("AI");
315   mp_ai.append(modelPath);
316
317   if (!mp.exists() && !mp_ai.exists()) {
318     SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
319     return true;
320   }
321
322   FGAIAircraft *aircraft = new FGAIAircraft(this);
323   aircraft->setPerformance(m_class); //"jet_transport";
324   aircraft->setCompany(airline); //i->getAirline();
325   aircraft->setAcType(acType); //i->getAcType();
326   aircraft->setPath(modelPath.c_str());
327   //aircraft->setFlightPlan(flightPlanName);
328   aircraft->setLatitude(position.getLatitudeDeg());
329   aircraft->setLongitude(position.getLongitudeDeg());
330   aircraft->setAltitude(flight->getCruiseAlt()*100); // convert from FL to feet
331   aircraft->setSpeed(speedKnots);
332   aircraft->setBank(0);
333       
334   courseToDest = SGGeodesy::courseDeg(position, arr->geod());
335   aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime, 
336                                                              dep, arr, true, radius, 
337                                                              flight->getCruiseAlt()*100, 
338                                                              position.getLatitudeDeg(), 
339                    position.getLongitudeDeg(), 
340                    speedKnots, flightType, acType, 
341                                                              airline));
342                    
343     
344   FGAIManager* aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");
345   aimgr->attach(aircraft);
346   AIManagerRef = aircraft->getID();
347   return true;
348 }
349
350 void FGAISchedule::scheduleFlights()
351 {
352   if (!flights.empty()) {
353     return;
354   }
355   
356   SG_LOG(SG_GENERAL, SG_BULK, "Scheduling for : " << modelPath << " " <<  registration << " " << homePort);
357   FGScheduledFlight *flight = NULL;
358   do {
359     flight = findAvailableFlight(currentDestination, flightIdentifier);
360     if (!flight) {
361       break;
362     }
363     
364     currentDestination = flight->getArrivalAirport()->getId();
365     if (!initialized) {
366         string departurePort = flight->getDepartureAirport()->getId();
367         if (fgGetString("/sim/presets/airport-id") == departurePort) {
368             hits++;
369         }
370         runCount++;
371         initialized = true;
372     }
373   
374     time_t arr, dep;
375     dep = flight->getDepartureTime();
376     arr = flight->getArrivalTime();
377     string depT = asctime(gmtime(&dep));
378     string arrT = asctime(gmtime(&arr));
379
380     depT = depT.substr(0,24);
381     arrT = arrT.substr(0,24);
382     SG_LOG(SG_GENERAL, SG_BULK, "  " << flight->getCallSign() << ":" 
383                              << "  " << flight->getDepartureAirport()->getId() << ":"
384                              << "  " << depT << ":"
385                              << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
386                              << "  " << arrT << ":");
387   
388     flights.push_back(flight);
389   } while (currentDestination != homePort);
390   SG_LOG(SG_GENERAL, SG_BULK, " Done ");
391 }
392
393 bool FGAISchedule::next()
394 {
395   if (!flights.empty()) {
396     flights.front()->release();
397     flights.erase(flights.begin());
398   }
399   
400   FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
401   if (!flight) {
402     return false;
403   }
404   
405   currentDestination = flight->getArrivalAirport()->getId();
406 /*
407   time_t arr, dep;
408   dep = flight->getDepartureTime();
409   arr = flight->getArrivalTime();
410   string depT = asctime(gmtime(&dep));
411   string arrT = asctime(gmtime(&arr));
412
413   depT = depT.substr(0,24);
414   arrT = arrT.substr(0,24);
415   //cerr << "  " << flight->getCallSign() << ":" 
416   //     << "  " << flight->getDepartureAirport()->getId() << ":"
417   //     << "  " << depT << ":"
418   //     << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
419   //     << "  " << arrT << ":" << endl;
420 */
421    flights.push_back(flight);
422    return true;
423 }
424
425 FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDestination,
426                                                       const string &req)
427 {
428     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
429
430     FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
431     FGScheduledFlightVecIterator fltBegin, fltEnd;
432     fltBegin = tmgr->getFirstFlight(req);
433     fltEnd   = tmgr->getLastFlight(req);
434
435
436      //cerr << "Finding available flight " << endl;
437      // For Now:
438      // Traverse every registered flight
439      if (fltBegin == fltEnd) {
440           //cerr << "No Flights Scheduled for " << req << endl;
441      }
442      int counter = 0;
443      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
444           (*i)->adjustTime(now);
445            //sort(fltBegin, fltEnd, compareScheduledFlights);
446            //cerr << counter++ << endl;
447      }
448      std::sort(fltBegin, fltEnd, compareScheduledFlights);
449      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
450           //bool valid = true;
451           counter++;
452           if (!(*i)->isAvailable()) {
453                //cerr << (*i)->getCallSign() << "is no longer available" << endl;
454                continue;
455           }
456           if (!((*i)->getRequirement() == req)) {
457                continue;
458           }
459           if (!(((*i)->getArrivalAirport()) && ((*i)->getDepartureAirport()))) {
460               continue;
461           }
462           if (!(currentDestination.empty())) {
463               if (currentDestination != (*i)->getDepartureAirport()->getId()) {
464                    //cerr << (*i)->getCallSign() << "Doesn't match destination" << endl;
465                    //cerr << "Current Destination " << currentDestination << "Doesnt match flight's " <<
466                    //          (*i)->getArrivalAirport()->getId() << endl;
467                    continue;
468               }
469           }
470           //TODO: check time
471           // So, if we actually get here, we have a winner
472           //cerr << "found flight: " << req << " : " << currentDestination << " : " <<       
473           //         (*i)->getArrivalAirport()->getId() << endl;
474           (*i)->lock();
475           return (*i);
476      }
477      // matches req?
478      // if currentDestination has a value, does it match departure of next flight?
479      // is departure time later than planned arrival?
480      // is departure port valid?
481      // is arrival port valid?
482      //cerr << "Ack no flight found: " << endl;
483      return NULL;
484 }
485
486 double FGAISchedule::getSpeed()
487 {
488   FGScheduledFlightVecIterator i = flights.begin();
489  
490   FGAirport* dep = (*i)->getDepartureAirport(),
491    *arr = (*i)->getArrivalAirport();
492   double dist = SGGeodesy::distanceNm(dep->geod(), arr->geod());
493   double remainingTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
494
495   double speed = dist / (remainingTimeEnroute/3600.0);
496   SG_CLAMP_RANGE(speed, 300.0, 500.0);
497   return speed;
498 }
499
500 void FGAISchedule::setScore   () 
501
502     if (runCount) {
503         score = ((double) hits / (double) runCount);
504     } else {
505         if (homePort == fgGetString("/sim/presets/airport-id")) {
506             score = 0.1;
507         } else {
508             score = 0.0;
509         }
510     }
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