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