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