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