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