]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/Schedule.cxx
Allow flights that arrive at their departure airport.
[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 using std::sort;
59
60 /******************************************************************************
61  * the FGAISchedule class contains data members and code to maintain a
62  * schedule of Flights for an articically controlled aircraft. 
63  *****************************************************************************/
64 FGAISchedule::FGAISchedule()
65 {
66   firstRun     = true;
67   AIManagerRef = 0;
68
69   heavy = false;
70   lat = 0;
71   lon = 0;
72   radius = 0;
73   groundOffset = 0;
74   distanceToUser = 0;
75   //score = 0;
76 }
77
78 /*
79 FGAISchedule::FGAISchedule(string    mdl, 
80                            string    liv, 
81                            string    reg, 
82                            bool      hvy, 
83                            string act, 
84                            string arln, 
85                            string mclass, 
86                            string fltpe,
87                            double rad,
88                            double grnd,
89                            int    scre,
90                            FGScheduledFlightVec flt)*/
91 FGAISchedule::FGAISchedule(string model, 
92                            string lvry,
93                            string port, 
94                            string reg, 
95                            string flightId,
96                            bool   hvy, 
97                            string act, 
98                            string arln, 
99                            string mclass, 
100                            string fltpe, 
101                            double rad, 
102                            double grnd)
103 {
104   modelPath        = model; 
105   livery           = lvry; 
106   homePort         = port;
107   registration     = reg;
108   flightIdentifier = flightId;
109   acType           = act;
110   airline          = arln;
111   m_class          = mclass;
112   flightType       = fltpe;
113   lat              = 0;
114   lon              = 0;
115   radius           = rad;
116   groundOffset     = grnd;
117   distanceToUser   = 0;
118   heavy            = hvy;
119   /*for (FGScheduledFlightVecIterator i = flt.begin();
120        i != flt.end();
121        i++)
122     flights.push_back(new FGScheduledFlight((*(*i))));*/
123   AIManagerRef     = 0;
124   //score    = scre;
125   firstRun         = true;
126 }
127
128 FGAISchedule::FGAISchedule(const FGAISchedule &other)
129 {
130   modelPath          = other.modelPath;
131   homePort           = other.homePort;
132   livery             = other.livery;
133   registration       = other.registration;
134   heavy              = other.heavy;
135   flightIdentifier   = other.flightIdentifier;
136   flights            = other.flights;
137   lat                = other.lat;
138   lon                = other.lon;
139   AIManagerRef       = other.AIManagerRef;
140   acType             = other.acType;
141   airline            = other.airline;
142   m_class            = other.m_class;
143   firstRun           = other.firstRun;
144   radius             = other.radius;
145   groundOffset       = other.groundOffset;
146   flightType         = other.flightType;
147   //score            = other.score;
148   distanceToUser     = other.distanceToUser;
149   currentDestination = other.currentDestination;
150   firstRun           = other.firstRun;
151 }
152
153
154 FGAISchedule::~FGAISchedule()
155 {
156 /*  for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++)
157     {
158       delete (*flt);
159     }
160   flights.clear();*/
161
162
163 bool FGAISchedule::init()
164 {
165   //tm targetTimeDate;
166   //SGTime* currTimeDate = globals->get_time_params();
167
168   //tm *temp = currTimeDate->getGmt();
169   //char buffer[512];
170   //sgTimeFormatTime(&targetTimeDate, buffer);
171   //cout << "Scheduled Time " << buffer << endl; 
172   //cout << "Time :" << time(NULL) << " SGTime : " << sgTimeGetGMT(temp) << endl;
173   /*for (FGScheduledFlightVecIterator i = flights.begin(); 
174        i != flights.end(); 
175        i++)
176     {
177       //i->adjustTime(now);
178       if (!((*i)->initializeAirports()))
179         return false;
180     } */
181   //sort(flights.begin(), flights.end());
182   // Since time isn't initialized yet when this function is called,
183   // Find the closest possible airport.
184   // This should give a reasonable initialization order. 
185   //setClosestDistanceToUser();
186   return true;
187 }
188
189 bool FGAISchedule::update(time_t now)
190
191   FGAirport *dep;
192   FGAirport *arr;
193   double angle;
194
195   FGAIManager *aimgr;
196   string airport;
197
198   double speed;
199
200   time_t 
201     totalTimeEnroute, 
202     elapsedTimeEnroute,
203     remainingTimeEnroute, deptime = 0;
204   double
205     userLatitude,
206     userLongitude;
207
208   SGVec3d newPos(0, 0, 0);
209
210
211   if (fgGetBool("/sim/traffic-manager/enabled") == false)
212     return true;
213   
214   aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");  
215     // Out-of-work aircraft seeks employment. Willing to work irregular hours ...
216     //cerr << "About to find a flight " << endl;
217     if (flights.empty()) {
218         //execute this loop at least once. 
219         SG_LOG(SG_GENERAL, SG_BULK, "Scheduling for : " << modelPath << " " <<  registration << " " << homePort);
220         FGScheduledFlight *flight = 0;
221          do {
222             flight = findAvailableFlight(currentDestination, flightIdentifier);
223             if (flight) {
224                 currentDestination = flight->getArrivalAirport()->getId();
225                 time_t arr, dep;
226                 dep = flight->getDepartureTime();
227                 arr = flight->getArrivalTime();
228                 string depT = asctime(gmtime(&dep));
229                 string arrT = asctime(gmtime(&arr));
230
231                 depT = depT.substr(0,24);
232                 arrT = arrT.substr(0,24);
233                 SG_LOG(SG_GENERAL, SG_BULK, "  " << flight->getCallSign() << ":" 
234                                          << "  " << flight->getDepartureAirport()->getId() << ":"
235                                          << "  " << depT << ":"
236                                          << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
237                                          << "  " << arrT << ":");
238             flights.push_back(flight);
239             }
240         } while ((currentDestination != homePort) && (flight != 0));
241         SG_LOG(SG_GENERAL, SG_BULK, cerr << " Done " << endl);
242     }
243     //cerr << " Done " << endl;
244    // No flights available for this aircraft
245   if (flights.size() == 0) {
246       return false;
247   }
248   // Sort all the scheduled flights according to scheduled departure time.
249   // Because this is done at every update, we only need to check the status
250   // of the first listed flight. 
251   //sort(flights.begin(), flights.end(), compareScheduledFlights);
252  if (firstRun) {
253      if (fgGetBool("/sim/traffic-manager/instantaneous-action") == true) {
254          deptime = now + rand() % 300; // Wait up to 5 minutes until traffic starts moving to prevent too many aircraft 
255                                    // from cluttering the gate areas.
256          cerr << "Scheduling " << registration << " for instantaneous action flight " << endl;
257      }
258      firstRun = false;
259   }
260   if (!deptime)
261     deptime = (*flights.begin())->getDepartureTime();
262   FGScheduledFlightVecIterator i = flights.begin();
263   SG_LOG (SG_GENERAL, SG_DEBUG,"Traffic Manager: Processing registration " << registration << " with callsign " << (*i)->getCallSign());
264   if (AIManagerRef)
265     {
266       // Check if this aircraft has been released. 
267       FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
268       if (tmgr->isReleased(AIManagerRef))
269         AIManagerRef = 0;
270     }
271
272   if (!AIManagerRef)
273     {
274       userLatitude  = fgGetDouble("/position/latitude-deg");
275       userLongitude = fgGetDouble("/position/longitude-deg");
276
277       //cerr << "Estimated minimum distance to user: " << distanceToUser << endl;
278       // This flight entry is entirely in the past, do we need to 
279       // push it forward in time to the next scheduled departure. 
280       if (((*i)->getDepartureTime() < now) && ((*i)->getArrivalTime() < now))
281         {
282           SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager:      Flight is in the Past");
283           //cerr << modelPath << " " <<  registration << ": Flights from the past belong to the past :-)" << endl;
284           //exit(1);
285           // Don't just update: check whether we need to load a new leg. etc.
286           // This update occurs for distant aircraft, so we can update the current leg
287           // and detach it from the current list of aircraft. 
288           (*i)->update();
289           i = flights.erase(i);
290           return true;
291         }
292
293       // Departure time in the past and arrival time in the future.
294       // This flight is in progress, so we need to calculate it's
295       // approximate position and -if in range- create an AIAircraft
296       // object for it. 
297       //if ((i->getDepartureTime() < now) && (i->getArrivalTime() > now))
298       
299       // Part of this flight is in the future.
300       if ((*i)->getArrivalTime() > now)
301         {
302           
303           dep = (*i)->getDepartureAirport();
304           arr = (*i)->getArrivalAirport  ();
305           if (!(dep && arr))
306             return false;
307           
308           if (dep != arr) {
309                SGVec3d a = SGVec3d::fromGeoc(SGGeoc::fromDegM(dep->getLongitude(),
310                                                      dep->getLatitude(), 1));
311                SGVec3d b = SGVec3d::fromGeoc(SGGeoc::fromDegM(arr->getLongitude(),
312                                                      arr->getLatitude(), 1));
313                SGVec3d _cross = cross(b, a);
314           
315                angle = sgACos(dot(a, b));
316           
317                // Okay, at this point we have the angle between departure and 
318                // arrival airport, in degrees. From here we can interpolate the
319                // position of the aircraft by calculating the ratio between 
320                // total time enroute and elapsed time enroute. 
321  
322                totalTimeEnroute     = (*i)->getArrivalTime() - (*i)->getDepartureTime();
323                if (now > (*i)->getDepartureTime())
324                {
325                    //err << "Lat = " << lat << ", lon = " << lon << endl;
326                    //cerr << "Time diff: " << now-i->getDepartureTime() << endl;
327                    elapsedTimeEnroute   = now - (*i)->getDepartureTime();
328                    remainingTimeEnroute = (*i)->getArrivalTime()   - now;  
329                    SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager:      Flight is in progress.");
330                }
331                else
332                {
333                    lat = dep->getLatitude();
334                    lon = dep->getLongitude();
335                    elapsedTimeEnroute = 0;
336                    remainingTimeEnroute = totalTimeEnroute;
337                    SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic Manager:      Flight is pending.");
338                 }
339                 angle *= ( (double) elapsedTimeEnroute/ (double) totalTimeEnroute);
340                 //cout << "a = " << a[0] << " " << a[1] << " " << a[2] 
341                 //     << "b = " << b[0] << " " << b[1] << " " << b[2] << endl;  
342                 sgdMat4 matrix;
343                 sgdMakeRotMat4(matrix, angle, _cross.data()); 
344                 for(int j = 0; j < 3; j++) {
345                     for (int k = 0; k<3; k++) {
346                         newPos[j] += matrix[j][k]*a[k];
347                     }
348                 }
349            }
350            SGGeod current;
351            if ((now > (*i)->getDepartureTime() && (dep != arr))) {
352                 current = SGGeod::fromCart(newPos);
353                 speed = SGGeodesy::distanceNm(current, arr->geod()) / 
354                                    ((double) remainingTimeEnroute/3600.0);
355            } else {
356                 current = dep->geod();
357                 speed = 450;
358            }
359            SGGeod user = SGGeod::fromDegM(userLongitude, userLatitude, (*i)->getCruiseAlt());
360            
361            distanceToUser = SGGeodesy::distanceNm(current, user);
362
363           // If distance between user and simulated aircaft is less
364           // then 500nm, create this flight. At jet speeds 500 nm is roughly
365           // one hour flight time, so that would be a good approximate point
366           // to start a more detailed simulation of this aircraft.
367           SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic manager: " << registration << " is scheduled for a flight from " 
368              << dep->getId() << " to " << arr->getId() << ". Current distance to user: " 
369              << distanceToUser);
370           if (distanceToUser < TRAFFICTOAIDISTTOSTART)
371             {
372               string flightPlanName = dep->getId() + string("-") + arr->getId() + 
373                 string(".xml");
374               SG_LOG (SG_GENERAL, SG_DEBUG, "Traffic manager: Creating AIModel");
375               //int alt;
376               //if  ((i->getDepartureTime() < now))
377               //{
378               //          alt = i->getCruiseAlt() *100;
379               //        }
380               //else
381               //{
382               //          alt = dep->_elevation+19;
383               //        }
384
385               // Only allow traffic to be created when the model path (or the AI version of mp) exists
386               SGPath mp(globals->get_fg_root());
387               SGPath mp_ai = mp;
388
389               mp.append(modelPath);
390               mp_ai.append("AI");
391               mp_ai.append(modelPath);
392
393               if (mp.exists() || mp_ai.exists())
394               {
395                   FGAIAircraft *aircraft = new FGAIAircraft(this);
396                   aircraft->setPerformance(m_class); //"jet_transport";
397                   aircraft->setCompany(airline); //i->getAirline();
398                   aircraft->setAcType(acType); //i->getAcType();
399                   aircraft->setPath(modelPath.c_str());
400                   //aircraft->setFlightPlan(flightPlanName);
401                   aircraft->setLatitude(lat);
402                   aircraft->setLongitude(lon);
403                   aircraft->setAltitude((*i)->getCruiseAlt()*100); // convert from FL to feet
404                   aircraft->setSpeed(speed);
405                   aircraft->setBank(0);
406       
407       courseToDest = SGGeodesy::courseDeg(current, arr->geod());
408                   aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime, 
409                                                              dep, arr,true, radius, 
410                                                              (*i)->getCruiseAlt()*100, 
411                                                              lat, lon, speed, flightType, acType, 
412                                                              airline));
413                   aimgr->attach(aircraft);
414                   
415                   
416                   AIManagerRef = aircraft->getID();
417                   //cerr << "Class: " << m_class << ". acType: " << acType << ". Airline: " << airline << ". Speed = " << speed << ". From " << dep->getId() << " to " << arr->getId() << ". Time Fraction = " << (remainingTimeEnroute/(double) totalTimeEnroute) << endl;
418                   //cerr << "Latitude : " << lat << ". Longitude : " << lon << endl;
419                   //cerr << "Dep      : " << dep->getLatitude()<< ", "<< dep->getLongitude() << endl;
420                   //cerr << "Arr      : " << arr->getLatitude()<< ", "<< arr->getLongitude() << endl;
421                   //cerr << "Time remaining = " << (remainingTimeEnroute/3600.0) << endl;
422                   //cerr << "Total time     = " << (totalTimeEnroute/3600.0) << endl;
423                   //cerr << "Distance remaining = " << distanceToDest*SG_METER_TO_NM << endl;
424                   }
425               else
426                 {
427                   SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
428                 }
429             }
430           return true;
431     }
432
433       // Both departure and arrival time are in the future, so this
434       // the aircraft is parked at the departure airport.
435       // Currently this status is mostly ignored, but in future
436       // versions, code should go here that -if within user range-
437       // positions these aircraft at parking locations at the airport.
438       if (((*i)->getDepartureTime() > now) && ((*i)->getArrivalTime() > now))
439         { 
440           dep = (*i)->getDepartureAirport();
441           return true;
442         } 
443     }
444   //cerr << "Traffic schedule got to beyond last clause" << endl;
445     // EMH: prevent a warning, should this be 'true' instead?
446     // DT: YES. Originally, this code couldn't be reached, but
447     // when the "if(!(AIManagerManager))" clause is false we
448     // fall through right to the end. This is a valid flow.
449     // the actual value is pretty innocent, only it triggers
450     // warning in TrafficManager::update().
451     // (which was added as a sanity check for myself in the first place. :-)
452     return true;
453 }
454
455
456 bool FGAISchedule::next()
457 {
458   FGScheduledFlightVecIterator i = flights.begin();
459   (*i)->release();
460   //FIXME: remove first entry, 
461   // load new flights until back at home airport
462   // Lock loaded flights
463   //sort(flights.begin(), flights.end(), compareScheduledFlights);
464   // until that time
465   i = flights.erase(i);
466   //cerr << "Next: scheduling for : " << modelPath << " " <<  registration << endl;
467   FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
468   if (flight) {
469       currentDestination = flight->getArrivalAirport()->getId();
470       time_t arr, dep;
471       dep = flight->getDepartureTime();
472       arr = flight->getArrivalTime();
473       string depT = asctime(gmtime(&dep));
474       string arrT = asctime(gmtime(&arr));
475
476       depT = depT.substr(0,24);
477       arrT = arrT.substr(0,24);
478       //cerr << "  " << flight->getCallSign() << ":" 
479       //     << "  " << flight->getDepartureAirport()->getId() << ":"
480       //     << "  " << depT << ":"
481       //     << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
482       //     << "  " << arrT << ":" << endl;
483
484        flights.push_back(flight);
485        return true;
486   } else {
487        return false;
488   }
489   //cerr << "FGAISchedule :: next needs updating" << endl;
490   //exit(1);
491 }
492
493 FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDestination,
494                                                       const string &req)
495 {
496     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
497
498     FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
499     FGScheduledFlightVecIterator fltBegin, fltEnd;
500     fltBegin = tmgr->getFirstFlight(req);
501     fltEnd   = tmgr->getLastFlight(req);
502
503
504      //cerr << "Finding available flight " << endl;
505      // For Now:
506      // Traverse every registered flight
507      if (fltBegin == fltEnd) {
508           //cerr << "No Flights Scheduled for " << req << endl;
509      }
510      int counter = 0;
511      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
512           (*i)->adjustTime(now);
513            //sort(fltBegin, fltEnd, compareScheduledFlights);
514            //cerr << counter++ << endl;
515      }
516      sort(fltBegin, fltEnd, compareScheduledFlights);
517      for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
518           //bool valid = true;
519           counter++;
520           if (!(*i)->isAvailable()) {
521                //cerr << (*i)->getCallSign() << "is no longer available" << endl;
522                continue;
523           }
524           if (!((*i)->getRequirement() == req)) {
525                continue;
526           }
527           if (!(((*i)->getArrivalAirport()) && ((*i)->getDepartureAirport()))) {
528               continue;
529           }
530           if (!(currentDestination.empty())) {
531               if (currentDestination != (*i)->getDepartureAirport()->getId()) {
532                    //cerr << (*i)->getCallSign() << "Doesn't match destination" << endl;
533                    //cerr << "Current Destination " << currentDestination << "Doesnt match flight's " <<
534                    //          (*i)->getArrivalAirport()->getId() << endl;
535                    continue;
536               }
537           }
538           //TODO: check time
539           // So, if we actually get here, we have a winner
540           //cerr << "found flight: " << req << " : " << currentDestination << " : " <<       
541           //         (*i)->getArrivalAirport()->getId() << endl;
542           (*i)->lock();
543           return (*i);
544      }
545      // matches req?
546      // if currentDestination has a value, does it match departure of next flight?
547      // is departure time later than planned arrival?
548      // is departure port valid?
549      // is arrival port valid?
550      //cerr << "Ack no flight found: " << endl;
551      return 0;
552 }
553
554 double FGAISchedule::getSpeed()
555 {
556   FGScheduledFlightVecIterator i = flights.begin();
557  
558   FGAirport* dep = (*i)->getDepartureAirport(),
559    *arr = (*i)->getArrivalAirport();
560   double dist = SGGeodesy::distanceNm(dep->geod(), arr->geod());
561   double remainingTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
562
563   double speed = dist / (remainingTimeEnroute/3600.0);
564   SG_CLAMP_RANGE(speed, 300.0, 500.0);
565   return speed;
566 }
567 /*
568 bool compareSchedules(FGAISchedule*a, FGAISchedule*b)
569
570   //return (*a) < (*b); 
571
572 */
573
574 // void FGAISchedule::setClosestDistanceToUser()
575 // {
576   
577   
578 //   double course;
579 //   double dist;
580
581 //   Point3D temp;
582 //   time_t 
583 //     totalTimeEnroute, 
584 //     elapsedTimeEnroute;
585  
586 //   double userLatitude  = fgGetDouble("/position/latitude-deg");
587 //   double userLongitude = fgGetDouble("/position/longitude-deg");
588
589 //   FGAirport *dep;
590   
591 // #if defined( __CYGWIN__) || defined( __MINGW32__)
592 //   #define HUGE HUGE_VAL
593 // #endif
594 //   distanceToUser = HUGE;
595 //   FGScheduledFlightVecIterator i = flights.begin();
596 //   while (i != flights.end())
597 //     {
598 //       dep = i->getDepartureAirport();
599 //       //if (!(dep))
600 //       //return HUGE;
601       
602 //       SGWayPoint user (   userLongitude,
603 //                        userLatitude,
604 //                        i->getCruiseAlt());
605 //       SGWayPoint current (dep->getLongitude(),
606 //                        dep->getLatitude(),
607 //                        0);
608 //       user.CourseAndDistance(current, &course, &dist);
609 //       if (dist < distanceToUser)
610 //      {
611 //        distanceToUser = dist;
612 //        //cerr << "Found closest distance to user for " << registration << " to be " << distanceToUser << " at airport " << dep->getId() << endl;
613 //      }
614 //       i++;
615 //     }
616 //   //return distToUser;
617 // }
618