]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/TrafficMgr.cxx
Merge branch 'maint2' into next
[flightgear.git] / src / Traffic / TrafficMgr.cxx
1 /******************************************************************************
2  * TrafficMGr.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  * Traffic manager parses airlines timetable-like data and uses this to 
24  * determine the approximate position of each AI aircraft in its database.
25  * When an AI aircraft is close to the user's position, a more detailed 
26  * AIModels based simulation is set up. 
27  * 
28  * I'm currently assuming the following simplifications:
29  * 1) The earth is a perfect sphere
30  * 2) Each aircraft flies a perfect great circle route.
31  * 3) Each aircraft flies at a constant speed (with infinite accelerations and
32  *    decelerations) 
33  * 4) Each aircraft leaves at exactly the departure time. 
34  * 5) Each aircraft arrives at exactly the specified arrival time. 
35  *
36  *
37  *****************************************************************************/
38
39 #ifdef HAVE_CONFIG_H
40 #  include "config.h"
41 #endif
42
43 #include <stdlib.h>
44 #include <time.h>
45 #include <iostream>
46 #include <fstream>
47
48
49 #include <string>
50 #include <vector>
51 #include <algorithm>
52
53 #include <plib/sg.h>
54 #include <plib/ul.h>
55
56 #include <simgear/compiler.h>
57 #include <simgear/math/polar3d.hxx>
58 #include <simgear/math/sg_geodesy.hxx>
59 #include <simgear/misc/sg_path.hxx>
60 #include <simgear/props/props.hxx>
61 #include <simgear/route/waypoint.hxx>
62 #include <simgear/structure/subsystem_mgr.hxx>
63 #include <simgear/xml/easyxml.hxx>
64
65 #include <AIModel/AIAircraft.hxx>
66 #include <AIModel/AIFlightPlan.hxx>
67 #include <AIModel/AIBase.hxx>
68 #include <Airports/simple.hxx>
69 #include <Main/fg_init.hxx>
70
71
72
73 #include "TrafficMgr.hxx"
74
75 using std::sort;
76  
77 /******************************************************************************
78  * TrafficManager
79  *****************************************************************************/
80 FGTrafficManager::FGTrafficManager()
81 {
82   //score = 0;
83   //runCount = 0;
84   acCounter = 0;
85 }
86
87 FGTrafficManager:: ~FGTrafficManager()
88 {
89   for (ScheduleVectorIterator sched = scheduledAircraft.begin(); sched != scheduledAircraft.end(); sched++)
90     {
91       delete (*sched);
92     }
93   scheduledAircraft.clear();
94   flights.clear();
95 }
96
97
98 void FGTrafficManager::init()
99
100   ulDir* d, *d2;
101   ulDirEnt* dent, *dent2;
102   SGPath aircraftDir = globals->get_fg_root();
103
104   SGPath path = aircraftDir;
105   
106   aircraftDir.append("AI/Traffic");
107   if ((d = ulOpenDir(aircraftDir.c_str())) != NULL)
108     {
109       while((dent = ulReadDir(d)) != NULL) {
110         if (string(dent->d_name) != string(".")  && 
111             string(dent->d_name) != string("..") &&
112             dent->d_isdir)
113           {
114             SGPath currACDir = aircraftDir;
115             currACDir.append(dent->d_name);
116             if ((d2 = ulOpenDir(currACDir.c_str())) == NULL)
117               return;
118             while ((dent2 = ulReadDir(d2)) != NULL) {
119               SGPath currFile = currACDir;
120               currFile.append(dent2->d_name);
121               if (currFile.extension() == string("xml"))
122                 {
123                   SGPath currFile = currACDir;
124                   currFile.append(dent2->d_name);
125                   SG_LOG(SG_GENERAL, SG_INFO, "Scanning " << currFile.str() << " for traffic");
126                   readXML(currFile.str(),*this);
127                 }
128             }
129             ulCloseDir(d2);
130           }
131       }
132       ulCloseDir(d);
133     }
134     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
135
136     currAircraft = scheduledAircraft.begin();
137     currAircraftClosest = scheduledAircraft.begin();
138 }
139
140 void FGTrafficManager::update(double /*dt*/)
141 {
142
143   time_t now = time(NULL) + fgGetLong("/sim/time/warp");
144   if (scheduledAircraft.size() == 0) {
145     return;
146   }
147   if(currAircraft == scheduledAircraft.end())
148     {
149       currAircraft = scheduledAircraft.begin();
150     }
151   if (!((*currAircraft)->update(now)))
152     {
153       // NOTE: With traffic manager II, this statement below is no longer true
154       // after proper initialization, we shouldnt get here.
155       // But let's make sure
156       //SG_LOG( SG_GENERAL, SG_ALERT, "Failed to update aircraft schedule in traffic manager");
157     }
158   currAircraft++;
159 }
160
161 void FGTrafficManager::release(int id)
162 {
163   releaseList.push_back(id);
164 }
165
166 bool FGTrafficManager::isReleased(int id)
167 {
168   IdListIterator i = releaseList.begin();
169   while (i != releaseList.end())
170     {
171       if ((*i) == id)
172         {
173           releaseList.erase(i);
174           return true;
175         }
176       i++;
177     }
178   return false;
179 }
180 /*
181 void FGTrafficManager::readTimeTableFromFile(SGPath infileName)
182 {
183     string model;
184     string livery;
185     string homePort;
186     string registration;
187     string flightReq;
188     bool   isHeavy;
189     string acType;
190     string airline;
191     string m_class;
192     string FlightType;
193     double radius;
194     double offset;
195
196     char buffer[256];
197     string buffString;
198     vector <string> tokens, depTime,arrTime;
199     vector <string>::iterator it;
200     ifstream infile(infileName.str().c_str());
201     while (1) {
202          infile.getline(buffer, 256);
203          if (infile.eof()) {
204              break;
205          }
206          //cerr << "Read line : " << buffer << endl;
207          buffString = string(buffer);
208          tokens.clear();
209          Tokenize(buffString, tokens, " \t");
210          //for (it = tokens.begin(); it != tokens.end(); it++) {
211          //    cerr << "Tokens: " << *(it) << endl;
212          //}
213          //cerr << endl;
214          if (!tokens.empty()) {
215              if (tokens[0] == string("AC")) {
216                  if (tokens.size() != 13) {
217                      SG_LOG(SG_GENERAL, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
218                      exit(1);
219                  }
220                  model          = tokens[12];
221                  livery         = tokens[6];
222                  homePort       = tokens[1];
223                  registration   = tokens[2];
224                  if (tokens[11] == string("false")) {
225                      isHeavy = false;
226                  } else {
227                      isHeavy = true;
228                  }
229                  acType         = tokens[4];
230                  airline        = tokens[5];
231                  flightReq      = tokens[3] + tokens[5];
232                  m_class        = tokens[10];
233                  FlightType     = tokens[9];
234                  radius         = atof(tokens[8].c_str());
235                  offset         = atof(tokens[7].c_str());;
236                  //cerr << "Found AC string " << model << " " << livery << " " << homePort << " " 
237                  //     << registration << " " << flightReq << " " << isHeavy << " " << acType << " " << airline << " " << m_class 
238                  //     << " " << FlightType << " " << radius << " " << offset << endl;
239                  scheduledAircraft.push_back(new FGAISchedule(model, 
240                                                               livery, 
241                                                               homePort,
242                                                               registration, 
243                                                               flightReq,
244                                                               isHeavy,
245                                                               acType, 
246                                                               airline, 
247                                                               m_class, 
248                                                               FlightType,
249                                                               radius,
250                                                               offset));
251              }
252              if (tokens[0] == string("FLIGHT")) {
253                  //cerr << "Found flight " << buffString << " size is : " << tokens.size() << endl;
254                  if (tokens.size() != 10) {
255                      SG_LOG(SG_GENERAL, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
256                      exit(1);
257                  }
258                  string callsign = tokens[1];
259                  string fltrules = tokens[2];
260                  string weekdays = tokens[3];
261                  string departurePort = tokens[5];
262                  string arrivalPort   = tokens[7];
263                  int    cruiseAlt     = atoi(tokens[8].c_str());
264                  string depTimeGen    = tokens[4];
265                  string arrTimeGen    = tokens[6];
266                  string repeat        = "WEEK";
267                  string requiredAircraft = tokens[9];
268                  
269                  if (weekdays.size() != 7) {
270                      cerr << "Found misconfigured weekdays string" << weekdays << endl;
271                      exit(1);
272                  }
273                  depTime.clear();
274                  arrTime.clear();
275                  Tokenize(depTimeGen, depTime, ":");
276                  Tokenize(arrTimeGen, arrTime, ":");
277                  double dep = atof(depTime[0].c_str()) + (atof(depTime[1].c_str()) / 60.0);
278                  double arr = atof(arrTime[0].c_str()) + (atof(arrTime[1].c_str()) / 60.0);
279                  //cerr << "Using " << dep << " " << arr << endl;
280                  bool arrivalWeekdayNeedsIncrement = false;
281                  if (arr < dep) {
282                        arrivalWeekdayNeedsIncrement = true;
283                  }
284                  for (int i = 0; i < 7; i++) {
285                      if (weekdays[i] != '.') {
286                          char buffer[4];
287                          snprintf(buffer, 4, "%d/", i);
288                          string departureTime = string(buffer) + depTimeGen + string(":00");
289                          string arrivalTime;
290                          if (!arrivalWeekdayNeedsIncrement) {
291                              arrivalTime   = string(buffer) + arrTimeGen + string(":00");
292                          }
293                          if (arrivalWeekdayNeedsIncrement && i != 6 ) {
294                              snprintf(buffer, 4, "%d/", i+1);
295                              arrivalTime   = string(buffer) + arrTimeGen + string(":00");
296                          }
297                          if (arrivalWeekdayNeedsIncrement && i == 6 ) {
298                              snprintf(buffer, 4, "%d/", 0);
299                              arrivalTime   = string(buffer) + arrTimeGen  + string(":00");
300                          }
301                          cerr << "Adding flight: " << callsign       << " "
302                                                    << fltrules       << " "
303                                                    <<  departurePort << " "
304                                                    <<  arrivalPort   << " "
305                                                    <<  cruiseAlt     << " "
306                                                    <<  departureTime << " "
307                                                    <<  arrivalTime   << " "
308                                                    <<  repeat        << " " 
309                                                    <<  requiredAircraft << endl;
310
311                          flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
312                                                                  fltrules,
313                                                                  departurePort,
314                                                                  arrivalPort,
315                                                                  cruiseAlt,
316                                                                  departureTime,
317                                                                  arrivalTime,
318                                                                  repeat,
319                                                                  requiredAircraft));
320                     }
321                 }
322              }
323          }
324
325     }
326     //exit(1);
327 }*/
328
329 /*
330 void FGTrafficManager::Tokenize(const string& str,
331                       vector<string>& tokens,
332                       const string& delimiters)
333 {
334     // Skip delimiters at beginning.
335     string::size_type lastPos = str.find_first_not_of(delimiters, 0);
336     // Find first "non-delimiter".
337     string::size_type pos     = str.find_first_of(delimiters, lastPos);
338
339     while (string::npos != pos || string::npos != lastPos)
340     {
341         // Found a token, add it to the vector.
342         tokens.push_back(str.substr(lastPos, pos - lastPos));
343         // Skip delimiters.  Note the "not_of"
344         lastPos = str.find_first_not_of(delimiters, pos);
345         // Find next "non-delimiter"
346         pos = str.find_first_of(delimiters, lastPos);
347     }
348 }
349 */
350
351 void  FGTrafficManager::startXML () {
352   //cout << "Start XML" << endl;
353   requiredAircraft = "";
354   homePort         = "";
355 }
356
357 void  FGTrafficManager::endXML () {
358   //cout << "End XML" << endl;
359 }
360
361 void  FGTrafficManager::startElement (const char * name, const XMLAttributes &atts) {
362   const char * attval;
363   //cout << "Start element " << name << endl;
364   //FGTrafficManager temp;
365   //for (int i = 0; i < atts.size(); i++)
366   //  if (string(atts.getName(i)) == string("include"))
367   attval = atts.getValue("include");
368   if (attval != 0)
369       {
370         //cout << "including " << attval << endl;
371         SGPath path = 
372           globals->get_fg_root();
373         path.append("/Traffic/");
374         path.append(attval);
375         readXML(path.str(), *this);
376       }
377   elementValueStack.push_back( "" );
378   //  cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl; 
379 }
380
381 void  FGTrafficManager::endElement (const char * name) {
382   //cout << "End element " << name << endl;
383   string element(name);
384   string value = elementValueStack.back();
385   elementValueStack.pop_back();
386
387   if (element == string("model"))
388     mdl = value;
389   else if (element == string("livery"))
390     livery = value;
391   else if (element == string("home-port"))
392     homePort = value;
393   else if (element == string("registration"))
394     registration = value;
395   else if (element == string("airline"))
396     airline = value;
397   else if (element == string("actype"))
398     acType = value;
399   else if (element == string("required-aircraft"))
400     requiredAircraft = value;
401   else if (element == string("flighttype"))
402     flighttype = value;
403   else if (element == string("radius"))
404     radius = atoi(value.c_str());
405   else if (element == string("offset"))
406     offset = atoi(value.c_str());
407   else if (element == string("performance-class"))
408     m_class = value;
409   else if (element == string("heavy"))
410     {
411       if(value == string("true"))
412         heavy = true;
413       else
414         heavy = false;
415     }
416   else if (element == string("callsign"))
417     callsign = value;
418   else if (element == string("fltrules"))
419     fltrules = value;
420   else if (element == string("port"))
421     port = value;
422   else if (element == string("time"))
423     timeString = value;
424   else if (element == string("departure"))
425     {
426       departurePort = port;
427       departureTime = timeString;
428     }
429   else if (element == string("cruise-alt"))
430     cruiseAlt = atoi(value.c_str());
431   else if (element == string("arrival"))
432     {
433       arrivalPort = port;
434       arrivalTime = timeString;
435     }
436   else if (element == string("repeat"))
437     repeat = value;
438   else if (element == string("flight"))
439     {
440       // We have loaded and parsed all the information belonging to this flight
441       // so we temporarily store it. 
442       //cerr << "Pusing back flight " << callsign << endl;
443       //cerr << callsign  <<  " " << fltrules     << " "<< departurePort << " " <<  arrivalPort << " "
444       //   << cruiseAlt <<  " " << departureTime<< " "<< arrivalTime   << " " << repeat << endl;
445
446       //Prioritize aircraft 
447       string apt = fgGetString("/sim/presets/airport-id");
448       //cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl;
449       //if (departurePort == apt) score++;
450       //flights.push_back(new FGScheduledFlight(callsign,
451         //                                fltrules,
452         //                                departurePort,
453         //                                arrivalPort,
454         //                                cruiseAlt,
455         //                                departureTime,
456         //                                arrivalTime,
457         //                                repeat));
458     if (requiredAircraft == "") {
459         char buffer[16];
460         snprintf(buffer, 16, "%d", acCounter);
461         requiredAircraft = buffer;
462     }
463     SG_LOG(SG_GENERAL, SG_DEBUG, "Adding flight: " << callsign       << " "
464                               << fltrules       << " "
465                               <<  departurePort << " "
466                               <<  arrivalPort   << " "
467                               <<  cruiseAlt     << " "
468                               <<  departureTime << " "
469                               <<  arrivalTime   << " "
470                               <<  repeat        << " " 
471                               <<  requiredAircraft);
472
473      flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
474                                                                  fltrules,
475                                                                  departurePort,
476                                                                  arrivalPort,
477                                                                  cruiseAlt,
478                                                                  departureTime,
479                                                                  arrivalTime,
480                                                                  repeat,
481                                                                  requiredAircraft));
482       requiredAircraft = "";
483   }
484   else if (element == string("aircraft"))
485     {
486       int proportion = (int) (fgGetDouble("/sim/traffic-manager/proportion") * 100);
487       int randval = rand() & 100;
488       if (randval < proportion) {
489           //scheduledAircraft.push_back(new FGAISchedule(mdl, 
490         //                                     livery, 
491         //                                     registration, 
492         //                                     heavy,
493         //                                     acType, 
494         //                                     airline, 
495         //                                     m_class, 
496         //                                     flighttype,
497         //                                     radius,
498         //                                     offset,
499         //                                     score,
500         //                                     flights));
501     if (requiredAircraft == "") {
502         char buffer[16];
503         snprintf(buffer, 16, "%d", acCounter);
504         requiredAircraft = buffer;
505     }
506     if (homePort == "") {
507         homePort = departurePort;
508     }
509             scheduledAircraft.push_back(new FGAISchedule(mdl, 
510                                                          livery, 
511                                                          homePort,
512                                                          registration, 
513                                                          requiredAircraft,
514                                                          heavy,
515                                                          acType, 
516                                                          airline, 
517                                                          m_class, 
518                                                          flighttype,
519                                                          radius,
520                                                          offset));
521
522      //  while(flights.begin() != flights.end()) {
523 //      flights.pop_back();
524 //       }
525         }
526     acCounter++;
527     requiredAircraft = "";
528     homePort = "";
529   //for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++)
530   //  {
531   //    delete (*flt);
532   //  }
533   //flights.clear();
534       SG_LOG( SG_GENERAL, SG_BULK, "Reading aircraft : " 
535               << registration 
536               << " with prioritization score " 
537               << score);
538       score = 0;
539     }
540 }
541
542 void  FGTrafficManager::data (const char * s, int len) {
543   string token = string(s,len);
544   //cout << "Character data " << string(s,len) << endl;
545   elementValueStack.back() += token;
546 }
547
548 void  FGTrafficManager::pi (const char * target, const char * data) {
549   //cout << "Processing instruction " << target << ' ' << data << endl;
550 }
551
552 void  FGTrafficManager::warning (const char * message, int line, int column) {
553   SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
554 }
555
556 void  FGTrafficManager::error (const char * message, int line, int column) {
557   SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
558 }
559