]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/Schedule.cxx
Fix two bugs in the new autopilot code
[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   
293   return createAIAircraft(flight, speed, deptime);
294 }
295
296 bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime)
297 {
298   FGAirport* dep = flight->getDepartureAirport();
299   FGAirport* arr = flight->getArrivalAirport();
300   string flightPlanName = dep->getId() + "-" + arr->getId() + ".xml";
301   SG_LOG(SG_GENERAL, SG_INFO, "Traffic manager: Creating AIModel from:" << flightPlanName);
302
303   // Only allow traffic to be created when the model path (or the AI version of mp) exists
304   SGPath mp(globals->get_fg_root());
305   SGPath mp_ai = mp;
306
307   mp.append(modelPath);
308   mp_ai.append("AI");
309   mp_ai.append(modelPath);
310
311   if (!mp.exists() && !mp_ai.exists()) {
312     SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
313     return true;
314   }
315
316   FGAIAircraft *aircraft = new FGAIAircraft(this);
317   aircraft->setPerformance(m_class); //"jet_transport";
318   aircraft->setCompany(airline); //i->getAirline();
319   aircraft->setAcType(acType); //i->getAcType();
320   aircraft->setPath(modelPath.c_str());
321   //aircraft->setFlightPlan(flightPlanName);
322   aircraft->setLatitude(position.getLatitudeDeg());
323   aircraft->setLongitude(position.getLongitudeDeg());
324   aircraft->setAltitude(flight->getCruiseAlt()*100); // convert from FL to feet
325   aircraft->setSpeed(speedKnots);
326   aircraft->setBank(0);
327       
328   courseToDest = SGGeodesy::courseDeg(position, arr->geod());
329   aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime, 
330                                                              dep, arr, true, radius, 
331                                                              flight->getCruiseAlt()*100, 
332                                                              position.getLatitudeDeg(), 
333                    position.getLongitudeDeg(), 
334                    speedKnots, flightType, acType, 
335                                                              airline));
336                    
337     
338   FGAIManager* aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");
339   aimgr->attach(aircraft);
340   AIManagerRef = aircraft->getID();
341   return true;
342 }
343
344 void FGAISchedule::scheduleFlights()
345 {
346   if (!flights.empty()) {
347     return;
348   }
349   
350   SG_LOG(SG_GENERAL, SG_BULK, "Scheduling for : " << modelPath << " " <<  registration << " " << homePort);
351   FGScheduledFlight *flight = NULL;
352   do {
353     flight = findAvailableFlight(currentDestination, flightIdentifier);
354     if (!flight) {
355       break;
356     }
357     
358     currentDestination = flight->getArrivalAirport()->getId();
359   
360     time_t arr, dep;
361     dep = flight->getDepartureTime();
362     arr = flight->getArrivalTime();
363     string depT = asctime(gmtime(&dep));
364     string arrT = asctime(gmtime(&arr));
365
366     depT = depT.substr(0,24);
367     arrT = arrT.substr(0,24);
368     SG_LOG(SG_GENERAL, SG_BULK, "  " << flight->getCallSign() << ":" 
369                              << "  " << flight->getDepartureAirport()->getId() << ":"
370                              << "  " << depT << ":"
371                              << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
372                              << "  " << arrT << ":");
373   
374     flights.push_back(flight);
375   } while (currentDestination != homePort);
376   SG_LOG(SG_GENERAL, SG_BULK, " Done ");
377 }
378
379 bool FGAISchedule::next()
380 {
381   if (!flights.empty()) {
382     flights.front()->release();
383     flights.erase(flights.begin());
384   }
385   
386   FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
387   if (!flight) {
388     return false;
389   }
390   
391   currentDestination = flight->getArrivalAirport()->getId();
392 /*
393   time_t arr, dep;
394   dep = flight->getDepartureTime();
395   arr = flight->getArrivalTime();
396   string depT = asctime(gmtime(&dep));
397   string arrT = asctime(gmtime(&arr));
398
399   depT = depT.substr(0,24);
400   arrT = arrT.substr(0,24);
401   //cerr << "  " << flight->getCallSign() << ":" 
402   //     << "  " << flight->getDepartureAirport()->getId() << ":"
403   //     << "  " << depT << ":"
404   //     << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
405   //     << "  " << arrT << ":" << endl;
406 */
407    flights.push_back(flight);
408    return true;
409 }
410
411 FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDestination,
412                                                       const string &req)
413 {
414     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
415
416     FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
417     FGScheduledFlightVecIterator fltBegin, fltEnd;
418     fltBegin = tmgr->getFirstFlight(req);
419     fltEnd   = tmgr->getLastFlight(req);
420
421
422      //cerr << "Finding available flight " << endl;
423      // For Now:
424      // Traverse every registered flight
425      if (fltBegin == fltEnd) {
426           //cerr << "No Flights Scheduled for " << req << endl;
427      }
428      int counter = 0;
429      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
430           (*i)->adjustTime(now);
431            //sort(fltBegin, fltEnd, compareScheduledFlights);
432            //cerr << counter++ << endl;
433      }
434      std::sort(fltBegin, fltEnd, compareScheduledFlights);
435      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
436           //bool valid = true;
437           counter++;
438           if (!(*i)->isAvailable()) {
439                //cerr << (*i)->getCallSign() << "is no longer available" << endl;
440                continue;
441           }
442           if (!((*i)->getRequirement() == req)) {
443                continue;
444           }
445           if (!(((*i)->getArrivalAirport()) && ((*i)->getDepartureAirport()))) {
446               continue;
447           }
448           if (!(currentDestination.empty())) {
449               if (currentDestination != (*i)->getDepartureAirport()->getId()) {
450                    //cerr << (*i)->getCallSign() << "Doesn't match destination" << endl;
451                    //cerr << "Current Destination " << currentDestination << "Doesnt match flight's " <<
452                    //          (*i)->getArrivalAirport()->getId() << endl;
453                    continue;
454               }
455           }
456           //TODO: check time
457           // So, if we actually get here, we have a winner
458           //cerr << "found flight: " << req << " : " << currentDestination << " : " <<       
459           //         (*i)->getArrivalAirport()->getId() << endl;
460           (*i)->lock();
461           return (*i);
462      }
463      // matches req?
464      // if currentDestination has a value, does it match departure of next flight?
465      // is departure time later than planned arrival?
466      // is departure port valid?
467      // is arrival port valid?
468      //cerr << "Ack no flight found: " << endl;
469      return NULL;
470 }
471
472 double FGAISchedule::getSpeed()
473 {
474   FGScheduledFlightVecIterator i = flights.begin();
475  
476   FGAirport* dep = (*i)->getDepartureAirport(),
477    *arr = (*i)->getArrivalAirport();
478   double dist = SGGeodesy::distanceNm(dep->geod(), arr->geod());
479   double remainingTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
480
481   double speed = dist / (remainingTimeEnroute/3600.0);
482   SG_CLAMP_RANGE(speed, 300.0, 500.0);
483   return speed;
484 }
485 /*
486 bool compareSchedules(FGAISchedule*a, FGAISchedule*b)
487
488   //return (*a) < (*b); 
489
490 */
491
492 // void FGAISchedule::setClosestDistanceToUser()
493 // {
494   
495   
496 //   double course;
497 //   double dist;
498
499 //   Point3D temp;
500 //   time_t 
501 //     totalTimeEnroute, 
502 //     elapsedTimeEnroute;
503  
504 //   double userLatitude  = fgGetDouble("/position/latitude-deg");
505 //   double userLongitude = fgGetDouble("/position/longitude-deg");
506
507 //   FGAirport *dep;
508   
509 // #if defined( __CYGWIN__) || defined( __MINGW32__)
510 //   #define HUGE HUGE_VAL
511 // #endif
512 //   distanceToUser = HUGE;
513 //   FGScheduledFlightVecIterator i = flights.begin();
514 //   while (i != flights.end())
515 //     {
516 //       dep = i->getDepartureAirport();
517 //       //if (!(dep))
518 //       //return HUGE;
519       
520 //       SGWayPoint user (   userLongitude,
521 //                        userLatitude,
522 //                        i->getCruiseAlt());
523 //       SGWayPoint current (dep->getLongitude(),
524 //                        dep->getLatitude(),
525 //                        0);
526 //       user.CourseAndDistance(current, &course, &dist);
527 //       if (dist < distanceToUser)
528 //      {
529 //        distanceToUser = dist;
530 //        //cerr << "Found closest distance to user for " << registration << " to be " << distanceToUser << " at airport " << dep->getId() << endl;
531 //      }
532 //       i++;
533 //     }
534 //   //return distToUser;
535 // }
536