]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/Schedule.cxx
PLIB net removed from FlightGear
[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        //cerr << "Scheduled " << registration <<  " " << score << " for Flight " 
368        //     << flight-> getCallSign() << " from " << departurePort << " to " << currentDestination << endl;
369         if (fgGetString("/sim/presets/airport-id") == departurePort) {
370             hits++;
371         }
372         //runCount++;
373         initialized = true;
374     }
375   
376     time_t arr, dep;
377     dep = flight->getDepartureTime();
378     arr = flight->getArrivalTime();
379     string depT = asctime(gmtime(&dep));
380     string arrT = asctime(gmtime(&arr));
381
382     depT = depT.substr(0,24);
383     arrT = arrT.substr(0,24);
384     SG_LOG(SG_GENERAL, SG_BULK, "  " << flight->getCallSign() << ":" 
385                              << "  " << flight->getDepartureAirport()->getId() << ":"
386                              << "  " << depT << ":"
387                              << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
388                              << "  " << arrT << ":");
389   
390     flights.push_back(flight);
391   } while (currentDestination != homePort);
392   SG_LOG(SG_GENERAL, SG_BULK, " Done ");
393 }
394
395 bool FGAISchedule::next()
396 {
397   if (!flights.empty()) {
398     flights.front()->release();
399     flights.erase(flights.begin());
400   }
401   
402   FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
403   if (!flight) {
404     return false;
405   }
406   
407   currentDestination = flight->getArrivalAirport()->getId();
408 /*
409   time_t arr, dep;
410   dep = flight->getDepartureTime();
411   arr = flight->getArrivalTime();
412   string depT = asctime(gmtime(&dep));
413   string arrT = asctime(gmtime(&arr));
414
415   depT = depT.substr(0,24);
416   arrT = arrT.substr(0,24);
417   //cerr << "  " << flight->getCallSign() << ":" 
418   //     << "  " << flight->getDepartureAirport()->getId() << ":"
419   //     << "  " << depT << ":"
420   //     << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
421   //     << "  " << arrT << ":" << endl;
422 */
423    flights.push_back(flight);
424    return true;
425 }
426
427 FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDestination,
428                                                       const string &req)
429 {
430     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
431
432     FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
433     FGScheduledFlightVecIterator fltBegin, fltEnd;
434     fltBegin = tmgr->getFirstFlight(req);
435     fltEnd   = tmgr->getLastFlight(req);
436
437
438      //cerr << "Finding available flight " << endl;
439      // For Now:
440      // Traverse every registered flight
441      if (fltBegin == fltEnd) {
442           //cerr << "No Flights Scheduled for " << req << endl;
443      }
444      int counter = 0;
445      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
446           (*i)->adjustTime(now);
447            //sort(fltBegin, fltEnd, compareScheduledFlights);
448            //cerr << counter++ << endl;
449      }
450      std::sort(fltBegin, fltEnd, compareScheduledFlights);
451      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
452           //bool valid = true;
453           counter++;
454           if (!(*i)->isAvailable()) {
455                //cerr << (*i)->getCallSign() << "is no longer available" << endl;
456                continue;
457           }
458           if (!((*i)->getRequirement() == req)) {
459                continue;
460           }
461           if (!(((*i)->getArrivalAirport()) && ((*i)->getDepartureAirport()))) {
462               continue;
463           }
464           if (!(currentDestination.empty())) {
465               if (currentDestination != (*i)->getDepartureAirport()->getId()) {
466                    //cerr << (*i)->getCallSign() << "Doesn't match destination" << endl;
467                    //cerr << "Current Destination " << currentDestination << "Doesnt match flight's " <<
468                    //          (*i)->getArrivalAirport()->getId() << endl;
469                    continue;
470               }
471           }
472           //TODO: check time
473           // So, if we actually get here, we have a winner
474           //cerr << "found flight: " << req << " : " << currentDestination << " : " <<       
475           //         (*i)->getArrivalAirport()->getId() << endl;
476           (*i)->lock();
477           return (*i);
478      }
479      // matches req?
480      // if currentDestination has a value, does it match departure of next flight?
481      // is departure time later than planned arrival?
482      // is departure port valid?
483      // is arrival port valid?
484      //cerr << "Ack no flight found: " << endl;
485      return NULL;
486 }
487
488 double FGAISchedule::getSpeed()
489 {
490   FGScheduledFlightVecIterator i = flights.begin();
491  
492   FGAirport* dep = (*i)->getDepartureAirport(),
493    *arr = (*i)->getArrivalAirport();
494   double dist = SGGeodesy::distanceNm(dep->geod(), arr->geod());
495   double remainingTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
496
497   double speed = dist / (remainingTimeEnroute/3600.0);
498   SG_CLAMP_RANGE(speed, 300.0, 500.0);
499   return speed;
500 }
501
502 void FGAISchedule::setScore   () 
503
504     if (runCount) {
505         score = ((double) hits / (double) runCount);
506     } else {
507         if (homePort == fgGetString("/sim/presets/airport-id")) {
508             score = 0.1;
509         } else {
510             score = 0.0;
511         }
512     }
513     runCount++;
514 }
515
516 bool compareSchedules(FGAISchedule*a, FGAISchedule*b)
517
518   return (*a) < (*b); 
519
520
521
522 // void FGAISchedule::setClosestDistanceToUser()
523 // {
524   
525   
526 //   double course;
527 //   double dist;
528
529 //   time_t 
530 //     totalTimeEnroute, 
531 //     elapsedTimeEnroute;
532  
533 //   double userLatitude  = fgGetDouble("/position/latitude-deg");
534 //   double userLongitude = fgGetDouble("/position/longitude-deg");
535
536 //   FGAirport *dep;
537   
538 // #if defined( __CYGWIN__) || defined( __MINGW32__)
539 //   #define HUGE HUGE_VAL
540 // #endif
541 //   distanceToUser = HUGE;
542 //   FGScheduledFlightVecIterator i = flights.begin();
543 //   while (i != flights.end())
544 //     {
545 //       dep = i->getDepartureAirport();
546 //       //if (!(dep))
547 //       //return HUGE;
548       
549 //       SGWayPoint user (   userLongitude,
550 //                        userLatitude,
551 //                        i->getCruiseAlt());
552 //       SGWayPoint current (dep->getLongitude(),
553 //                        dep->getLatitude(),
554 //                        0);
555 //       user.CourseAndDistance(current, &course, &dist);
556 //       if (dist < distanceToUser)
557 //      {
558 //        distanceToUser = dist;
559 //        //cerr << "Found closest distance to user for " << registration << " to be " << distanceToUser << " at airport " << dep->getId() << endl;
560 //      }
561 //       i++;
562 //     }
563 //   //return distToUser;
564 // }
565