]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/TrafficMgr.cxx
Fix a Clang warning, checking signed char as if it was unsigned.
[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 <cstring>
46 #include <iostream>
47 #include <fstream>
48
49
50 #include <string>
51 #include <vector>
52 #include <algorithm>
53 #include <boost/foreach.hpp>
54
55 #include <simgear/compiler.h>
56 #include <simgear/misc/sg_path.hxx>
57 #include <simgear/misc/sg_dir.hxx>
58 #include <simgear/props/props.hxx>
59 #include <simgear/structure/subsystem_mgr.hxx>
60 #include <simgear/xml/easyxml.hxx>
61
62 #include <AIModel/AIAircraft.hxx>
63 #include <AIModel/AIFlightPlan.hxx>
64 #include <AIModel/AIBase.hxx>
65 #include <Airports/simple.hxx>
66 #include <Main/fg_init.hxx>
67
68
69
70 #include "TrafficMgr.hxx"
71
72 using std::sort;
73 using std::strcmp;
74
75 /******************************************************************************
76  * TrafficManager
77  *****************************************************************************/
78 FGTrafficManager::FGTrafficManager() :
79   inited(false),
80   doingInit(false),
81   enabled("/sim/traffic-manager/enabled"),
82   aiEnabled("/sim/ai/enabled"),
83   realWxEnabled("/environment/realwx/enabled"),
84   metarValid("/environment/metar/valid")
85 {
86     //score = 0;
87     //runCount = 0;
88     acCounter = 0;
89 }
90
91 FGTrafficManager::~FGTrafficManager()
92 {
93     shutdown();
94 }
95
96 void FGTrafficManager::shutdown()
97 {
98     // Save the heuristics data
99     bool saveData = false;
100     ofstream cachefile;
101     if (fgGetBool("/sim/traffic-manager/heuristics")) {
102         SGPath cacheData(fgGetString("/sim/fg-home"));
103         cacheData.append("ai");
104         string airport = fgGetString("/sim/presets/airport-id");
105
106         if ((airport) != "") {
107             char buffer[128];
108             ::snprintf(buffer, 128, "%c/%c/%c/",
109                        airport[0], airport[1], airport[2]);
110             cacheData.append(buffer);
111             if (!cacheData.exists()) {
112                 cacheData.create_dir(0777);
113             }
114             cacheData.append(airport + "-cache.txt");
115             //cerr << "Saving AI traffic heuristics" << endl;
116             saveData = true;
117             cachefile.open(cacheData.str().c_str());
118             cachefile << "[TrafficManagerCachedata:ref:2011:09:04]" << endl;
119         }
120     }
121     for (ScheduleVectorIterator sched = scheduledAircraft.begin();
122          sched != scheduledAircraft.end(); sched++) {
123         if (saveData) {
124             cachefile << (*sched)->getRegistration() << " "
125                 << (*sched)->getRunCount() << " "
126                 << (*sched)->getHits() << " "
127                 << (*sched)->getLastUsed() << endl;
128         }
129         delete(*sched);
130     }
131     if (saveData) {
132         cachefile.close();
133     }
134     scheduledAircraft.clear();
135     flights.clear();
136     releaseList.clear();
137
138     currAircraft = scheduledAircraft.begin();
139     doingInit = false;
140     inited = false;
141 }
142
143
144 void FGTrafficManager::init()
145 {
146     if (!enabled) {
147       return;
148     }
149
150     assert(!doingInit);
151     doingInit = true;
152     if (string(fgGetString("/sim/traffic-manager/datafile")) == string("")) {
153         simgear::Dir trafficDir(SGPath(globals->get_fg_root(), "AI/Traffic"));
154         simgear::PathList d = trafficDir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
155         
156         BOOST_FOREACH(SGPath p, d) {
157           simgear::Dir d2(p);
158           simgear::PathList trafficFiles = d2.children(simgear::Dir::TYPE_FILE, ".xml");
159           schedulesToRead.insert(schedulesToRead.end(), trafficFiles.begin(), trafficFiles.end());
160         }
161     } else {
162         fgSetBool("/sim/traffic-manager/heuristics", false);
163         SGPath path = string(fgGetString("/sim/traffic-manager/datafile"));
164         string ext = path.extension();
165         if (path.extension() == "xml") {
166             if (path.exists()) {
167                 readXML(path.str(), *this);
168             }
169         } else if (path.extension() == "conf") {
170             if (path.exists()) {
171                 readTimeTableFromFile(path);
172             }
173         } else {
174              SG_LOG(SG_AI, SG_ALERT,
175                                "Unknown data format " << path.str()
176                                 << " for traffic");
177         }
178         //exit(1);
179     }
180 }
181
182 void FGTrafficManager::initStep()
183 {
184     assert(doingInit);
185     if (schedulesToRead.empty()) {
186         finishInit();
187         return;
188     }
189     
190     SGPath path = schedulesToRead.front();
191     schedulesToRead.erase(schedulesToRead.begin());
192     SG_LOG(SG_AI, SG_DEBUG, path << " for traffic");
193     readXML(path.str(), *this);
194 }
195
196 void FGTrafficManager::finishInit()
197 {
198     assert(doingInit);
199     SG_LOG(SG_AI, SG_INFO, "finishing AI-Traffic init");
200     loadHeuristics();
201     
202     // Do sorting and scoring separately, to take advantage of the "homeport| variable
203     for (currAircraft = scheduledAircraft.begin();
204          currAircraft != scheduledAircraft.end(); currAircraft++) {
205         (*currAircraft)->setScore();
206     }
207     
208     sort(scheduledAircraft.begin(), scheduledAircraft.end(),
209          compareSchedules);
210     currAircraft = scheduledAircraft.begin();
211     currAircraftClosest = scheduledAircraft.begin();
212     
213     doingInit = false;
214     inited = true;
215 }
216
217 void FGTrafficManager::loadHeuristics()
218 {
219     if (!fgGetBool("/sim/traffic-manager/heuristics")) {
220         return;
221     }
222   
223     HeuristicMap heurMap;
224     //cerr << "Processing Heuristics" << endl;
225     // Load the heuristics data
226     SGPath cacheData(fgGetString("/sim/fg-home"));
227     cacheData.append("ai");
228     string airport = fgGetString("/sim/presets/airport-id");
229     if ((airport) != "") {
230       char buffer[128];
231       ::snprintf(buffer, 128, "%c/%c/%c/",
232                  airport[0], airport[1], airport[2]);
233       cacheData.append(buffer);
234       cacheData.append(airport + "-cache.txt");
235       string revisionStr;
236       if (cacheData.exists()) {
237         ifstream data(cacheData.c_str());
238         data >> revisionStr;
239         if (revisionStr != "[TrafficManagerCachedata:ref:2011:09:04]") {
240           SG_LOG(SG_GENERAL, SG_ALERT,"Traffic Manager Warning: discarding outdated cachefile " << 
241                  cacheData.c_str() << " for Airport " << airport);
242         } else {
243           while (1) {
244             Heuristic h; // = new Heuristic;
245             data >> h.registration >> h.runCount >> h.hits >> h.lastRun;
246             if (data.eof())
247               break;
248             HeuristicMapIterator itr = heurMap.find(h.registration);
249             if (itr != heurMap.end()) {
250               SG_LOG(SG_GENERAL, SG_WARN,"Traffic Manager Warning: found duplicate tailnumber " << 
251                      h.registration << " for AI aircraft");
252             } else {
253               heurMap[h.registration] = h;
254             }
255           }
256         }
257       }
258     } 
259     
260   for(currAircraft = scheduledAircraft.begin(); currAircraft != scheduledAircraft.end(); ++currAircraft) {
261         string registration = (*currAircraft)->getRegistration();
262         HeuristicMapIterator itr = heurMap.find(registration);
263         if (itr != heurMap.end()) {
264             (*currAircraft)->setrunCount(itr->second.runCount);
265             (*currAircraft)->setHits(itr->second.hits);
266             (*currAircraft)->setLastUsed(itr->second.lastRun);
267         }
268     }
269 }
270
271 void FGTrafficManager::update(double /*dt */ )
272 {
273     if (!enabled)
274     {
275         if (inited || doingInit)
276             shutdown();
277         return;
278     }
279
280     if ((realWxEnabled && !metarValid)) {
281         return;
282     }
283
284     if (!aiEnabled)
285     {
286         // traffic depends on AI module
287         aiEnabled = true;
288     }
289
290     if (!inited) {
291         if (!doingInit) {
292             init();
293         }
294         
295         initStep();
296         if (!inited) {
297             return; // still more to do on next update() call
298         }
299     }
300         
301     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
302     if (scheduledAircraft.size() == 0) {
303         return;
304     }
305
306     SGVec3d userCart = globals->get_aircraft_positon_cart();
307
308     if (currAircraft == scheduledAircraft.end()) {
309         currAircraft = scheduledAircraft.begin();
310     }
311     //cerr << "Processing << " << (*currAircraft)->getRegistration() << " with score " << (*currAircraft)->getScore() << endl;
312     if (!((*currAircraft)->update(now, userCart))) {
313         (*currAircraft)->taint();
314     }
315     currAircraft++;
316 }
317
318 void FGTrafficManager::release(int id)
319 {
320     releaseList.push_back(id);
321 }
322
323 bool FGTrafficManager::isReleased(int id)
324 {
325     IdListIterator i = releaseList.begin();
326     while (i != releaseList.end()) {
327         if ((*i) == id) {
328             releaseList.erase(i);
329             return true;
330         }
331         i++;
332     }
333     return false;
334 }
335
336
337 void FGTrafficManager::readTimeTableFromFile(SGPath infileName)
338 {
339     string model;
340     string livery;
341     string homePort;
342     string registration;
343     string flightReq;
344     bool   isHeavy;
345     string acType;
346     string airline;
347     string m_class;
348     string FlightType;
349     double radius;
350     double offset;
351
352     char buffer[256];
353     string buffString;
354     vector <string> tokens, depTime,arrTime;
355     vector <string>::iterator it;
356     ifstream infile(infileName.str().c_str());
357     while (1) {
358          infile.getline(buffer, 256);
359          if (infile.eof()) {
360              break;
361          }
362          //cerr << "Read line : " << buffer << endl;
363          buffString = string(buffer);
364          tokens.clear();
365          Tokenize(buffString, tokens, " \t");
366          //for (it = tokens.begin(); it != tokens.end(); it++) {
367          //    cerr << "Tokens: " << *(it) << endl;
368          //}
369          //cerr << endl;
370          if (!tokens.empty()) {
371              if (tokens[0] == string("AC")) {
372                  if (tokens.size() != 13) {
373                      SG_LOG(SG_GENERAL, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
374                      exit(1);
375                  }
376                  model          = tokens[12];
377                  livery         = tokens[6];
378                  homePort       = tokens[1];
379                  registration   = tokens[2];
380                  if (tokens[11] == string("false")) {
381                      isHeavy = false;
382                  } else {
383                      isHeavy = true;
384                  }
385                  acType         = tokens[4];
386                  airline        = tokens[5];
387                  flightReq      = tokens[3] + tokens[5];
388                  m_class        = tokens[10];
389                  FlightType     = tokens[9];
390                  radius         = atof(tokens[8].c_str());
391                  offset         = atof(tokens[7].c_str());;
392                  
393                  if (!FGAISchedule::validModelPath(model)) {
394                      SG_LOG(SG_GENERAL, SG_WARN, "TrafficMgr: Missing model path:" << 
395                             model << " from " << infileName.str());
396                  } else {
397                  
398                  SG_LOG(SG_GENERAL, SG_INFO, "Adding Aircraft" << model << " " << livery << " " << homePort << " " 
399                                                                 << registration << " " << flightReq << " " << isHeavy 
400                                                                 << " " << acType << " " << airline << " " << m_class 
401                                                                 << " " << FlightType << " " << radius << " " << offset);
402                  scheduledAircraft.push_back(new FGAISchedule(model, 
403                                                               livery, 
404                                                               homePort,
405                                                               registration, 
406                                                               flightReq,
407                                                               isHeavy,
408                                                               acType, 
409                                                               airline, 
410                                                               m_class, 
411                                                               FlightType,
412                                                               radius,
413                                                               offset));
414                  } // of valid model path
415              }
416              if (tokens[0] == string("FLIGHT")) {
417                  //cerr << "Found flight " << buffString << " size is : " << tokens.size() << endl;
418                  if (tokens.size() != 10) {
419                      SG_LOG(SG_GENERAL, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
420                      exit(1);
421                  }
422                  string callsign = tokens[1];
423                  string fltrules = tokens[2];
424                  string weekdays = tokens[3];
425                  string departurePort = tokens[5];
426                  string arrivalPort   = tokens[7];
427                  int    cruiseAlt     = atoi(tokens[8].c_str());
428                  string depTimeGen    = tokens[4];
429                  string arrTimeGen    = tokens[6];
430                  string repeat        = "WEEK";
431                  string requiredAircraft = tokens[9];
432
433                  if (weekdays.size() != 7) {
434                      SG_LOG(SG_GENERAL, SG_ALERT, "Found misconfigured weekdays string" << weekdays);
435                      exit(1);
436                  }
437                  depTime.clear();
438                  arrTime.clear();
439                  Tokenize(depTimeGen, depTime, ":");
440                  Tokenize(arrTimeGen, arrTime, ":");
441                  double dep = atof(depTime[0].c_str()) + (atof(depTime[1].c_str()) / 60.0);
442                  double arr = atof(arrTime[0].c_str()) + (atof(arrTime[1].c_str()) / 60.0);
443                  //cerr << "Using " << dep << " " << arr << endl;
444                  bool arrivalWeekdayNeedsIncrement = false;
445                  if (arr < dep) {
446                        arrivalWeekdayNeedsIncrement = true;
447                  }
448                  for (int i = 0; i < 7; i++) {
449                      int j = i+1;
450                      if (weekdays[i] != '.') {
451                          char buffer[4];
452                          snprintf(buffer, 4, "%d/", j);
453                          string departureTime = string(buffer) + depTimeGen + string(":00");
454                          string arrivalTime;
455                          if (!arrivalWeekdayNeedsIncrement) {
456                              arrivalTime   = string(buffer) + arrTimeGen + string(":00");
457                          }
458                          if (arrivalWeekdayNeedsIncrement && i != 6 ) {
459                              snprintf(buffer, 4, "%d/", j+1);
460                              arrivalTime   = string(buffer) + arrTimeGen + string(":00");
461                          }
462                          if (arrivalWeekdayNeedsIncrement && i == 6 ) {
463                              snprintf(buffer, 4, "%d/", 0);
464                              arrivalTime   = string(buffer) + arrTimeGen  + string(":00");
465                          }
466                          SG_LOG(SG_GENERAL, SG_ALERT, "Adding flight " << callsign       << " "
467                                                       << fltrules       << " "
468                                                       <<  departurePort << " "
469                                                       <<  arrivalPort   << " "
470                                                       <<  cruiseAlt     << " "
471                                                       <<  departureTime << " "
472                                                       <<  arrivalTime   << " "
473                                                       << repeat        << " " 
474                                                       <<  requiredAircraft);
475
476                          flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
477                                                                  fltrules,
478                                                                  departurePort,
479                                                                  arrivalPort,
480                                                                  cruiseAlt,
481                                                                  departureTime,
482                                                                  arrivalTime,
483                                                                  repeat,
484                                                                  requiredAircraft));
485                     }
486                 }
487              }
488          }
489
490     }
491     //exit(1);
492 }
493
494
495 void FGTrafficManager::Tokenize(const string& str,
496                       vector<string>& tokens,
497                       const string& delimiters)
498 {
499     // Skip delimiters at beginning.
500     string::size_type lastPos = str.find_first_not_of(delimiters, 0);
501     // Find first "non-delimiter".
502     string::size_type pos     = str.find_first_of(delimiters, lastPos);
503
504     while (string::npos != pos || string::npos != lastPos)
505     {
506         // Found a token, add it to the vector.
507         tokens.push_back(str.substr(lastPos, pos - lastPos));
508         // Skip delimiters.  Note the "not_of"
509         lastPos = str.find_first_not_of(delimiters, pos);
510         // Find next "non-delimiter"
511         pos = str.find_first_of(delimiters, lastPos);
512     }
513 }
514
515
516 void FGTrafficManager::startXML()
517 {
518     //cout << "Start XML" << endl;
519     requiredAircraft = "";
520     homePort = "";
521 }
522
523 void FGTrafficManager::endXML()
524 {
525     //cout << "End XML" << endl;
526 }
527
528 void FGTrafficManager::startElement(const char *name,
529                                     const XMLAttributes & atts)
530 {
531     const char *attval;
532     //cout << "Start element " << name << endl;
533     //FGTrafficManager temp;
534     //for (int i = 0; i < atts.size(); i++)
535     //  if (string(atts.getName(i)) == string("include"))
536     attval = atts.getValue("include");
537     if (attval != 0) {
538         //cout << "including " << attval << endl;
539         SGPath path = globals->get_fg_root();
540         path.append("/Traffic/");
541         path.append(attval);
542         readXML(path.str(), *this);
543     }
544     elementValueStack.push_back("");
545     //  cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl; 
546 }
547
548 void FGTrafficManager::endElement(const char *name)
549 {
550     //cout << "End element " << name << endl;
551     const string & value = elementValueStack.back();
552
553     if (!strcmp(name, "model"))
554         mdl = value;
555     else if (!strcmp(name, "livery"))
556         livery = value;
557     else if (!strcmp(name, "home-port"))
558         homePort = value;
559     else if (!strcmp(name, "registration"))
560         registration = value;
561     else if (!strcmp(name, "airline"))
562         airline = value;
563     else if (!strcmp(name, "actype"))
564         acType = value;
565     else if (!strcmp(name, "required-aircraft"))
566         requiredAircraft = value;
567     else if (!strcmp(name, "flighttype"))
568         flighttype = value;
569     else if (!strcmp(name, "radius"))
570         radius = atoi(value.c_str());
571     else if (!strcmp(name, "offset"))
572         offset = atoi(value.c_str());
573     else if (!strcmp(name, "performance-class"))
574         m_class = value;
575     else if (!strcmp(name, "heavy")) {
576         if (value == string("true"))
577             heavy = true;
578         else
579             heavy = false;
580     } else if (!strcmp(name, "callsign"))
581         callsign = value;
582     else if (!strcmp(name, "fltrules"))
583         fltrules = value;
584     else if (!strcmp(name, "port"))
585         port = value;
586     else if (!strcmp(name, "time"))
587         timeString = value;
588     else if (!strcmp(name, "departure")) {
589         departurePort = port;
590         departureTime = timeString;
591     } else if (!strcmp(name, "cruise-alt"))
592         cruiseAlt = atoi(value.c_str());
593     else if (!strcmp(name, "arrival")) {
594         arrivalPort = port;
595         arrivalTime = timeString;
596     } else if (!strcmp(name, "repeat"))
597         repeat = value;
598     else if (!strcmp(name, "flight")) {
599         // We have loaded and parsed all the information belonging to this flight
600         // so we temporarily store it. 
601         //cerr << "Pusing back flight " << callsign << endl;
602         //cerr << callsign  <<  " " << fltrules     << " "<< departurePort << " " <<  arrivalPort << " "
603         //   << cruiseAlt <<  " " << departureTime<< " "<< arrivalTime   << " " << repeat << endl;
604
605         //Prioritize aircraft 
606         string apt = fgGetString("/sim/presets/airport-id");
607         //cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl;
608         //if (departurePort == apt) score++;
609         //flights.push_back(new FGScheduledFlight(callsign,
610         //                                fltrules,
611         //                                departurePort,
612         //                                arrivalPort,
613         //                                cruiseAlt,
614         //                                departureTime,
615         //                                arrivalTime,
616         //                                repeat));
617         if (requiredAircraft == "") {
618             char buffer[16];
619             snprintf(buffer, 16, "%d", acCounter);
620             requiredAircraft = buffer;
621         }
622         SG_LOG(SG_GENERAL, SG_DEBUG, "Adding flight: " << callsign << " "
623                << fltrules << " "
624                << departurePort << " "
625                << arrivalPort << " "
626                << cruiseAlt << " "
627                << departureTime << " "
628                << arrivalTime << " " << repeat << " " << requiredAircraft);
629         // For database maintainance purposes, it may be convenient to
630         // 
631         if (fgGetBool("/sim/traffic-manager/dumpdata") == true) {
632              SG_LOG(SG_GENERAL, SG_ALERT, "Traffic Dump FLIGHT," << callsign << ","
633                           << fltrules << ","
634                           << departurePort << ","
635                           << arrivalPort << ","
636                           << cruiseAlt << ","
637                           << departureTime << ","
638                           << arrivalTime << "," << repeat << "," << requiredAircraft);
639         }
640         flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
641                                                                   fltrules,
642                                                                   departurePort,
643                                                                   arrivalPort,
644                                                                   cruiseAlt,
645                                                                   departureTime,
646                                                                   arrivalTime,
647                                                                   repeat,
648                                                                   requiredAircraft));
649         requiredAircraft = "";
650     } else if (!strcmp(name, "aircraft")) {
651         endAircraft();
652     }
653     
654     elementValueStack.pop_back();
655 }
656
657 void FGTrafficManager::endAircraft()
658 {
659     string isHeavy = heavy ? "true" : "false";
660
661     if (missingModels.find(mdl) != missingModels.end()) {
662     // don't stat() or warn again
663         requiredAircraft = homePort = "";
664         return;
665     }
666     
667     if (!FGAISchedule::validModelPath(mdl)) {
668         missingModels.insert(mdl);
669         SG_LOG(SG_GENERAL, SG_WARN, "TrafficMgr: Missing model path:" << mdl);
670         requiredAircraft = homePort = "";
671         return;
672     }
673         
674     int proportion =
675         (int) (fgGetDouble("/sim/traffic-manager/proportion") * 100);
676     int randval = rand() & 100;
677     if (randval > proportion) {
678         requiredAircraft = homePort = "";
679         return;
680     }
681     
682     if (fgGetBool("/sim/traffic-manager/dumpdata") == true) {
683         SG_LOG(SG_GENERAL, SG_ALERT, "Traffic Dump AC," << homePort << "," << registration << "," << requiredAircraft 
684                << "," << acType << "," << livery << "," 
685                << airline << ","  << m_class << "," << offset << "," << radius << "," << flighttype << "," << isHeavy << "," << mdl);
686     }
687
688     if (requiredAircraft == "") {
689         char buffer[16];
690         snprintf(buffer, 16, "%d", acCounter);
691         requiredAircraft = buffer;
692     }
693     if (homePort == "") {
694         homePort = departurePort;
695     }
696     
697     scheduledAircraft.push_back(new FGAISchedule(mdl,
698                                                  livery,
699                                                  homePort,
700                                                  registration,
701                                                  requiredAircraft,
702                                                  heavy,
703                                                  acType,
704                                                  airline,
705                                                  m_class,
706                                                  flighttype,
707                                                  radius, offset));
708             
709     acCounter++;
710     requiredAircraft = "";
711     homePort = "";
712     SG_LOG(SG_GENERAL, SG_BULK, "Reading aircraft : "
713            << registration << " with prioritization score " << score);
714     score = 0;
715 }
716     
717 void FGTrafficManager::data(const char *s, int len)
718 {
719     string token = string(s, len);
720     //cout << "Character data " << string(s,len) << endl;
721     elementValueStack.back() += token;
722 }
723
724 void FGTrafficManager::pi(const char *target, const char *data)
725 {
726     //cout << "Processing instruction " << target << ' ' << data << endl;
727 }
728
729 void FGTrafficManager::warning(const char *message, int line, int column)
730 {
731     SG_LOG(SG_IO, SG_WARN,
732            "Warning: " << message << " (" << line << ',' << column << ')');
733 }
734
735 void FGTrafficManager::error(const char *message, int line, int column)
736 {
737     SG_LOG(SG_IO, SG_ALERT,
738            "Error: " << message << " (" << line << ',' << column << ')');
739 }