]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/TrafficMgr.cxx
ATISEncoder: minor tweak
[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/structure/exception.hxx>
61
62 #include <simgear/xml/easyxml.hxx>
63 #include <simgear/threads/SGThread.hxx>
64 #include <simgear/threads/SGGuard.hxx>
65 #include <simgear/scene/tsync/terrasync.hxx>
66
67 #include <AIModel/AIAircraft.hxx>
68 #include <AIModel/AIFlightPlan.hxx>
69 #include <AIModel/AIBase.hxx>
70 #include <Airports/airport.hxx>
71 #include <Main/fg_init.hxx>
72
73 #include "TrafficMgr.hxx"
74
75 using std::sort;
76 using std::strcmp;
77 using std::endl;
78 using std::string;
79 using std::vector;
80
81 /**
82  * Thread encapsulating parsing the traffic schedules. 
83  */
84 class ScheduleParseThread : public SGThread, public XMLVisitor
85 {
86 public:
87   ScheduleParseThread(FGTrafficManager* traffic) :
88     _trafficManager(traffic),
89     _isFinished(false),
90     _cancelThread(false),
91     cruiseAlt(0),
92     score(0),
93     acCounter(0),
94     radius(0),
95     offset(0),
96     heavy(false)
97   {
98     
99   }
100   
101   // if we're destroyed while running, ensure the thread exits cleanly
102   ~ScheduleParseThread()
103   {
104     _lock.lock();
105     if (!_isFinished) {
106       _cancelThread = true; // request cancellation so we don't wait ages
107       _lock.unlock();
108       join();
109     } else {
110       _lock.unlock();
111     }
112   }
113   
114   void setTrafficDirs(const PathList& dirs)
115   {
116     _trafficDirPaths = dirs;
117   }
118   
119   bool isFinished() const
120   {
121     SGGuard<SGMutex> g(_lock);
122     return _isFinished;
123   }
124   
125   virtual void run()
126   {
127       BOOST_FOREACH(SGPath p, _trafficDirPaths) {
128           parseTrafficDir(p);
129           if (_cancelThread) {
130               return;
131           }
132       }
133     
134     SGGuard<SGMutex> g(_lock);
135     _isFinished = true;
136   }
137     
138     void startXML()
139     {
140         //cout << "Start XML" << endl;
141         requiredAircraft = "";
142         homePort = "";
143     }
144     
145     void endXML()
146     {
147         //cout << "End XML" << endl;
148     }
149     
150     void startElement(const char *name,
151                                         const XMLAttributes & atts)
152     {
153         const char *attval;
154         //cout << "Start element " << name << endl;
155         //FGTrafficManager temp;
156         //for (int i = 0; i < atts.size(); i++)
157         //  if (string(atts.getName(i)) == string("include"))
158         attval = atts.getValue("include");
159         if (attval != 0) {
160             //cout << "including " << attval << endl;
161             SGPath path = globals->get_fg_root();
162             path.append("/Traffic/");
163             path.append(attval);
164             readXML(path.str(), *this);
165         }
166         elementValueStack.push_back("");
167         //  cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl;
168     }
169     
170     void endElement(const char *name)
171     {
172         //cout << "End element " << name << endl;
173         const string & value = elementValueStack.back();
174         
175         if (!strcmp(name, "model"))
176             mdl = value;
177         else if (!strcmp(name, "livery"))
178             livery = value;
179         else if (!strcmp(name, "home-port"))
180             homePort = value;
181         else if (!strcmp(name, "registration"))
182             registration = value;
183         else if (!strcmp(name, "airline"))
184             airline = value;
185         else if (!strcmp(name, "actype"))
186             acType = value;
187         else if (!strcmp(name, "required-aircraft"))
188             requiredAircraft = value;
189         else if (!strcmp(name, "flighttype"))
190             flighttype = value;
191         else if (!strcmp(name, "radius"))
192             radius = atoi(value.c_str());
193         else if (!strcmp(name, "offset"))
194             offset = atoi(value.c_str());
195         else if (!strcmp(name, "performance-class"))
196             m_class = value;
197         else if (!strcmp(name, "heavy")) {
198             if (value == string("true"))
199                 heavy = true;
200             else
201                 heavy = false;
202         } else if (!strcmp(name, "callsign"))
203             callsign = value;
204         else if (!strcmp(name, "fltrules"))
205             fltrules = value;
206         else if (!strcmp(name, "port"))
207             port = value;
208         else if (!strcmp(name, "time"))
209             timeString = value;
210         else if (!strcmp(name, "departure")) {
211             departurePort = port;
212             departureTime = timeString;
213         } else if (!strcmp(name, "cruise-alt"))
214             cruiseAlt = atoi(value.c_str());
215         else if (!strcmp(name, "arrival")) {
216             arrivalPort = port;
217             arrivalTime = timeString;
218         } else if (!strcmp(name, "repeat"))
219             repeat = value;
220         else if (!strcmp(name, "flight")) {
221             // We have loaded and parsed all the information belonging to this flight
222             // so we temporarily store it.
223             //cerr << "Pusing back flight " << callsign << endl;
224             //cerr << callsign  <<  " " << fltrules     << " "<< departurePort << " " <<  arrivalPort << " "
225             //   << cruiseAlt <<  " " << departureTime<< " "<< arrivalTime   << " " << repeat << endl;
226             
227             //Prioritize aircraft
228             string apt = fgGetString("/sim/presets/airport-id");
229             //cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl;
230             //if (departurePort == apt) score++;
231             //flights.push_back(new FGScheduledFlight(callsign,
232             //                                fltrules,
233             //                                departurePort,
234             //                                arrivalPort,
235             //                                cruiseAlt,
236             //                                departureTime,
237             //                                arrivalTime,
238             //                                repeat));
239             if (requiredAircraft == "") {
240                 char buffer[16];
241                 snprintf(buffer, 16, "%d", acCounter);
242                 requiredAircraft = buffer;
243             }
244             SG_LOG(SG_AI, SG_DEBUG, "Adding flight: " << callsign << " "
245                    << fltrules << " "
246                    << departurePort << " "
247                    << arrivalPort << " "
248                    << cruiseAlt << " "
249                    << departureTime << " "
250                    << arrivalTime << " " << repeat << " " << requiredAircraft);
251             // For database maintainance purposes, it may be convenient to
252             //
253             if (fgGetBool("/sim/traffic-manager/dumpdata") == true) {
254                 SG_LOG(SG_AI, SG_ALERT, "Traffic Dump FLIGHT," << callsign << ","
255                        << fltrules << ","
256                        << departurePort << ","
257                        << arrivalPort << ","
258                        << cruiseAlt << ","
259                        << departureTime << ","
260                        << arrivalTime << "," << repeat << "," << requiredAircraft);
261             }
262             
263             _trafficManager->flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
264                                                                       fltrules,
265                                                                       departurePort,
266                                                                       arrivalPort,
267                                                                       cruiseAlt,
268                                                                       departureTime,
269                                                                       arrivalTime,
270                                                                       repeat,
271                                                                       requiredAircraft));
272             requiredAircraft = "";
273         } else if (!strcmp(name, "aircraft")) {
274             endAircraft();
275         }
276         
277         elementValueStack.pop_back();
278     }
279
280     
281     void data(const char *s, int len)
282     {
283         string token = string(s, len);
284         //cout << "Character data " << string(s,len) << endl;
285         elementValueStack.back() += token;
286     }
287     
288     void pi(const char *target, const char *data)
289     {
290         //cout << "Processing instruction " << target << ' ' << data << endl;
291     }
292     
293     void warning(const char *message, int line, int column)
294     {
295         SG_LOG(SG_IO, SG_WARN,
296                "Warning: " << message << " (" << line << ',' << column << ')');
297     }
298     
299     void error(const char *message, int line, int column)
300     {
301         SG_LOG(SG_IO, SG_ALERT,
302                "Error: " << message << " (" << line << ',' << column << ')');
303     }
304     
305 private:
306     void endAircraft()
307     {
308         string isHeavy = heavy ? "true" : "false";
309         
310         if (missingModels.find(mdl) != missingModels.end()) {
311             // don't stat() or warn again
312             requiredAircraft = homePort = "";
313             return;
314         }
315         
316         if (!FGAISchedule::validModelPath(mdl)) {
317             missingModels.insert(mdl);
318             SG_LOG(SG_AI, SG_WARN, "TrafficMgr: Missing model path:" << mdl);
319             requiredAircraft = homePort = "";
320             return;
321         }
322         
323         int proportion =
324         (int) (fgGetDouble("/sim/traffic-manager/proportion") * 100);
325         int randval = rand() & 100;
326         if (randval > proportion) {
327             requiredAircraft = homePort = "";
328             return;
329         }
330         
331         if (fgGetBool("/sim/traffic-manager/dumpdata") == true) {
332             SG_LOG(SG_AI, SG_ALERT, "Traffic Dump AC," << homePort << "," << registration << "," << requiredAircraft
333                    << "," << acType << "," << livery << ","
334                    << airline << ","  << m_class << "," << offset << "," << radius << "," << flighttype << "," << isHeavy << "," << mdl);
335         }
336         
337         if (requiredAircraft == "") {
338             char buffer[16];
339             snprintf(buffer, 16, "%d", acCounter);
340             requiredAircraft = buffer;
341         }
342         if (homePort == "") {
343             homePort = departurePort;
344         }
345         
346         // caution, modifying the scheduled aircraft strucutre from the
347         // 'wrong' thread. This is safe becuase FGTrafficManager won't touch
348         // the structure while we exist.
349         _trafficManager->scheduledAircraft.push_back(new FGAISchedule(mdl,
350                                                      livery,
351                                                      homePort,
352                                                      registration,
353                                                      requiredAircraft,
354                                                      heavy,
355                                                      acType,
356                                                      airline,
357                                                      m_class,
358                                                      flighttype,
359                                                      radius, offset));
360         
361         acCounter++;
362         requiredAircraft = "";
363         homePort = "";
364         score = 0;
365     }
366     
367     void parseTrafficDir(const SGPath& path)
368     {
369         SGTimeStamp st;
370         st.stamp();
371         
372         simgear::Dir trafficDir(path);
373         simgear::PathList d = trafficDir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
374         
375         BOOST_FOREACH(SGPath p, d) {
376             simgear::Dir d2(p);
377             SG_LOG(SG_AI, SG_INFO, "parsing traffic in:" << p);
378             simgear::PathList trafficFiles = d2.children(simgear::Dir::TYPE_FILE, ".xml");
379             BOOST_FOREACH(SGPath xml, trafficFiles) {                
380                 readXML(xml.str(), *this);
381                 if (_cancelThread) {
382                     return;
383                 }
384             }
385         } // of sub-directories iteration
386         
387         SG_LOG(SG_AI, SG_INFO, "parsing traffic schedules took:" << st.elapsedMSec() << "msec");
388     }
389     
390   FGTrafficManager* _trafficManager;
391   mutable SGMutex _lock;
392   bool _isFinished;
393   bool _cancelThread;
394   PathList _trafficDirPaths;
395    
396 // parser state
397     
398     string_list elementValueStack;
399     // record model paths which are missing, to avoid duplicate
400     // warnings when parsing traffic schedules.
401     std::set<std::string> missingModels;
402     
403     std::string mdl, livery, registration, callsign, fltrules,
404     port, timeString, departurePort, departureTime, arrivalPort, arrivalTime,
405     repeat, acType, airline, m_class, flighttype, requiredAircraft, homePort;
406     int cruiseAlt;
407     int score, acCounter;
408     double radius, offset;
409     bool heavy;
410
411 };
412
413 /******************************************************************************
414  * TrafficManager
415  *****************************************************************************/
416 FGTrafficManager::FGTrafficManager() :
417   inited(false),
418   doingInit(false),
419   trafficSyncRequested(false),
420   waitingMetarTime(0.0),
421   enabled("/sim/traffic-manager/enabled"),
422   aiEnabled("/sim/ai/enabled"),
423   realWxEnabled("/environment/realwx/enabled"),
424   metarValid("/environment/metar/valid")
425 {
426 }
427
428 FGTrafficManager::~FGTrafficManager()
429 {
430     shutdown();
431 }
432
433 void FGTrafficManager::shutdown()
434 {
435     if (!inited) {
436       if (doingInit) {
437         scheduleParser.reset();
438         doingInit = false;
439       }
440       
441       return;
442     }
443   
444     // Save the heuristics data
445     bool saveData = false;
446     std::ofstream cachefile;
447     if (fgGetBool("/sim/traffic-manager/heuristics")) {
448         SGPath cacheData(globals->get_fg_home());
449         cacheData.append("ai");
450         const string airport = fgGetString("/sim/presets/airport-id");
451
452         if ((airport) != "") {
453             char buffer[128];
454             ::snprintf(buffer, 128, "%c/%c/%c/",
455                        airport[0], airport[1], airport[2]);
456             cacheData.append(buffer);
457             if (!cacheData.exists()) {
458                 cacheData.create_dir(0755);
459             }
460             cacheData.append(airport + "-cache.txt");
461             //cerr << "Saving AI traffic heuristics" << endl;
462             saveData = true;
463             cachefile.open(cacheData.str().c_str());
464             cachefile << "[TrafficManagerCachedata:ref:2011:09:04]" << endl;
465         }
466     }
467   
468     BOOST_FOREACH(FGAISchedule* acft, scheduledAircraft) {
469         if (saveData) {
470             cachefile << acft->getRegistration() << " "
471                 << acft->getRunCount() << " "
472                 << acft->getHits() << " "
473                 << acft->getLastUsed() << endl;
474         }
475         delete acft;
476     }
477     if (saveData) {
478         cachefile.close();
479     }
480     scheduledAircraft.clear();
481     flights.clear();
482
483     currAircraft = scheduledAircraft.begin();
484     doingInit = false;
485     inited = false;
486     trafficSyncRequested = false;
487 }
488
489 void FGTrafficManager::init()
490 {
491     if (!enabled) {
492       return;
493     }
494
495     assert(!doingInit);
496     simgear::SGTerraSync* terraSync = static_cast<simgear::SGTerraSync*>(globals->get_subsystem("terrasync"));
497     bool doDataSync = fgGetBool("/sim/terrasync/ai-data-enabled");
498     if (doDataSync && terraSync) {
499         if (!trafficSyncRequested) {
500             SG_LOG(SG_AI, SG_INFO, "Sync of AI traffic via TerraSync enabled");
501             terraSync->scheduleDataDir("AI/Traffic");
502             trafficSyncRequested = true;
503         }
504         
505         if (terraSync->isDataDirPending("AI/Traffic")) {
506             return; // remain in the init state
507         }
508         
509         SG_LOG(SG_AI, SG_INFO, "Traffic files sync complete");
510     }
511     
512     doingInit = true;
513     if (string(fgGetString("/sim/traffic-manager/datafile")).empty()) {
514         PathList dirs = globals->get_data_paths("AI/Traffic");
515         
516         // temporary flag to restrict loading while traffic data is found
517         // through terrasync /and/ fgdata. Ultimatley we *do* want to be able to
518         // overlay sources.
519         
520         if (dirs.size() > 1) {
521             SGPath p = dirs.back();
522             if (simgear::strutils::starts_with(p.str(), globals->get_fg_root())) {
523                 dirs.pop_back();
524             }
525         }
526         
527         if (dirs.empty()) {
528             doingInit = false;
529             return;
530         }
531         
532         scheduleParser.reset(new ScheduleParseThread(this));
533         scheduleParser->setTrafficDirs(dirs);
534         scheduleParser->start();
535     } else {
536         fgSetBool("/sim/traffic-manager/heuristics", false);
537         SGPath path = string(fgGetString("/sim/traffic-manager/datafile"));
538         string ext = path.extension();
539         if (path.extension() == "xml") {
540             if (path.exists()) {
541                 // use a SchedulerParser to parse, but run it in this thread,
542                 // i.e don't start it
543                 ScheduleParseThread parser(this);
544                 readXML(path.str(), parser);
545             }
546         } else if (path.extension() == "conf") {
547             if (path.exists()) {
548                 readTimeTableFromFile(path);
549             }
550         } else {
551              SG_LOG(SG_AI, SG_ALERT,
552                                "Unknown data format " << path.str()
553                                 << " for traffic");
554         }
555         //exit(1);
556     }
557 }
558
559
560
561 void FGTrafficManager::finishInit()
562 {
563     assert(doingInit);
564     SG_LOG(SG_AI, SG_INFO, "finishing AI-Traffic init");
565     loadHeuristics();
566     
567     // Do sorting and scoring separately, to take advantage of the "homeport" variable
568     BOOST_FOREACH(FGAISchedule* schedule, scheduledAircraft) {
569         schedule->setScore();
570     }
571     
572     sort(scheduledAircraft.begin(), scheduledAircraft.end(),
573          compareSchedules);
574     currAircraft = scheduledAircraft.begin();
575     currAircraftClosest = scheduledAircraft.begin();
576     
577     doingInit = false;
578     inited = true;
579 }
580
581 void FGTrafficManager::loadHeuristics()
582 {
583     if (!fgGetBool("/sim/traffic-manager/heuristics")) {
584         return;
585     }
586   
587     HeuristicMap heurMap;
588     //cerr << "Processing Heuristics" << endl;
589     // Load the heuristics data
590     SGPath cacheData(globals->get_fg_home());
591     cacheData.append("ai");
592     string airport = fgGetString("/sim/presets/airport-id");
593     if ((airport) != "") {
594       char buffer[128];
595       ::snprintf(buffer, 128, "%c/%c/%c/",
596                  airport[0], airport[1], airport[2]);
597       cacheData.append(buffer);
598       cacheData.append(airport + "-cache.txt");
599       string revisionStr;
600       if (cacheData.exists()) {
601         std::ifstream data(cacheData.c_str());
602         data >> revisionStr;
603         if (revisionStr != "[TrafficManagerCachedata:ref:2011:09:04]") {
604           SG_LOG(SG_AI, SG_ALERT,"Traffic Manager Warning: discarding outdated cachefile " <<
605                  cacheData.c_str() << " for Airport " << airport);
606         } else {
607           while (1) {
608             Heuristic h; // = new Heuristic;
609             data >> h.registration >> h.runCount >> h.hits >> h.lastRun;
610             if (data.eof())
611               break;
612             HeuristicMapIterator itr = heurMap.find(h.registration);
613             if (itr != heurMap.end()) {
614               SG_LOG(SG_AI, SG_WARN,"Traffic Manager Warning: found duplicate tailnumber " <<
615                      h.registration << " for AI aircraft");
616             } else {
617               heurMap[h.registration] = h;
618             }
619           }
620         }
621       }
622     } 
623     
624   for(currAircraft = scheduledAircraft.begin(); currAircraft != scheduledAircraft.end(); ++currAircraft) {
625         const string& registration = (*currAircraft)->getRegistration();
626         HeuristicMapIterator itr = heurMap.find(registration);
627         if (itr != heurMap.end()) {
628             (*currAircraft)->setrunCount(itr->second.runCount);
629             (*currAircraft)->setHits(itr->second.hits);
630             (*currAircraft)->setLastUsed(itr->second.lastRun);
631         }
632     }
633 }
634
635 bool FGTrafficManager::metarReady(double dt)
636 {
637     // wait for valid METAR (when realWX is enabled only), since we need
638     // to know the active runway
639     if (metarValid || !realWxEnabled)
640     {
641         waitingMetarTime = 0.0;
642         return true;
643     }
644
645     // METAR timeout: when running offline, remote server is down etc
646     if (waitingMetarStation != fgGetString("/environment/metar/station-id"))
647     {
648         // station has changed: wait for reply, restart timeout
649         waitingMetarTime = 0.0;
650         waitingMetarStation = fgGetString("/environment/metar/station-id");
651         return false;
652     }
653
654     // timeout elapsed (10 seconds)?
655     if (waitingMetarTime > 20.0)
656     {
657         return true;
658     }
659
660     waitingMetarTime += dt;
661     return false;
662 }
663
664 void FGTrafficManager::update(double dt)
665 {
666     if (!enabled)
667     {
668         if (inited || doingInit)
669             shutdown();
670         return;
671     }
672
673     if (!metarReady(dt))
674         return;
675
676     if (!aiEnabled)
677     {
678         // traffic depends on AI module
679         aiEnabled = true;
680     }
681
682     if (!inited) {
683         if (!doingInit) {
684             init();
685         }
686                 
687         if (!doingInit || !scheduleParser->isFinished()) {
688           return;
689         }
690       
691         finishInit();
692     }
693         
694     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
695     if (scheduledAircraft.empty()) {
696         return;
697     }
698
699     SGVec3d userCart = globals->get_aircraft_position_cart();
700
701     if (currAircraft == scheduledAircraft.end()) {
702         currAircraft = scheduledAircraft.begin();
703     }
704
705     //cerr << "Processing << " << (*currAircraft)->getRegistration() << " with score " << (*currAircraft)->getScore() << endl;
706     if ((*currAircraft)->update(now, userCart)) {
707         // schedule is done - process another aircraft in next iteration
708         currAircraft++;
709     }
710 }
711
712 void FGTrafficManager::readTimeTableFromFile(SGPath infileName)
713 {
714     string model;
715     string livery;
716     string homePort;
717     string registration;
718     string flightReq;
719     bool   isHeavy;
720     string acType;
721     string airline;
722     string m_class;
723     string FlightType;
724     double radius;
725     double offset;
726
727     char buffer[256];
728     string buffString;
729     vector <string> tokens, depTime,arrTime;
730     vector <string>::iterator it;
731     std::ifstream infile(infileName.str().c_str());
732     while (1) {
733          infile.getline(buffer, 256);
734          if (infile.eof()) {
735              break;
736          }
737          //cerr << "Read line : " << buffer << endl;
738          buffString = string(buffer);
739          tokens.clear();
740          Tokenize(buffString, tokens, " \t");
741          //for (it = tokens.begin(); it != tokens.end(); it++) {
742          //    cerr << "Tokens: " << *(it) << endl;
743          //}
744          //cerr << endl;
745          if (!tokens.empty()) {
746              if (tokens[0] == string("AC")) {
747                  if (tokens.size() != 13) {
748                      throw sg_io_exception("Error parsing traffic file @ " + buffString, sg_location(infileName.str()));
749                  }
750                  
751                  
752                  model          = tokens[12];
753                  livery         = tokens[6];
754                  homePort       = tokens[1];
755                  registration   = tokens[2];
756                  if (tokens[11] == string("false")) {
757                      isHeavy = false;
758                  } else {
759                      isHeavy = true;
760                  }
761                  acType         = tokens[4];
762                  airline        = tokens[5];
763                  flightReq      = tokens[3] + tokens[5];
764                  m_class        = tokens[10];
765                  FlightType     = tokens[9];
766                  radius         = atof(tokens[8].c_str());
767                  offset         = atof(tokens[7].c_str());;
768                  
769                  if (!FGAISchedule::validModelPath(model)) {
770                      SG_LOG(SG_AI, SG_WARN, "TrafficMgr: Missing model path:" <<
771                             model << " from " << infileName.str());
772                  } else {
773                  
774                  SG_LOG(SG_AI, SG_INFO, "Adding Aircraft" << model << " " << livery << " " << homePort << " "
775                                                                 << registration << " " << flightReq << " " << isHeavy 
776                                                                 << " " << acType << " " << airline << " " << m_class 
777                                                                 << " " << FlightType << " " << radius << " " << offset);
778                  scheduledAircraft.push_back(new FGAISchedule(model, 
779                                                               livery, 
780                                                               homePort,
781                                                               registration, 
782                                                               flightReq,
783                                                               isHeavy,
784                                                               acType, 
785                                                               airline, 
786                                                               m_class, 
787                                                               FlightType,
788                                                               radius,
789                                                               offset));
790                  } // of valid model path
791              }
792              if (tokens[0] == string("FLIGHT")) {
793                  //cerr << "Found flight " << buffString << " size is : " << tokens.size() << endl;
794                  if (tokens.size() != 10) {
795                      SG_LOG(SG_AI, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
796                      exit(1);
797                  }
798                  string callsign = tokens[1];
799                  string fltrules = tokens[2];
800                  string weekdays = tokens[3];
801                  string departurePort = tokens[5];
802                  string arrivalPort   = tokens[7];
803                  int    cruiseAlt     = atoi(tokens[8].c_str());
804                  string depTimeGen    = tokens[4];
805                  string arrTimeGen    = tokens[6];
806                  string repeat        = "WEEK";
807                  string requiredAircraft = tokens[9];
808
809                  if (weekdays.size() != 7) {
810                      SG_LOG(SG_AI, SG_ALERT, "Found misconfigured weekdays string" << weekdays);
811                      exit(1);
812                  }
813                  depTime.clear();
814                  arrTime.clear();
815                  Tokenize(depTimeGen, depTime, ":");
816                  Tokenize(arrTimeGen, arrTime, ":");
817                  double dep = atof(depTime[0].c_str()) + (atof(depTime[1].c_str()) / 60.0);
818                  double arr = atof(arrTime[0].c_str()) + (atof(arrTime[1].c_str()) / 60.0);
819                  //cerr << "Using " << dep << " " << arr << endl;
820                  bool arrivalWeekdayNeedsIncrement = false;
821                  if (arr < dep) {
822                        arrivalWeekdayNeedsIncrement = true;
823                  }
824                  for (int i = 0; i < 7; i++) {
825                      int j = i+1;
826                      if (weekdays[i] != '.') {
827                          char buffer[4];
828                          snprintf(buffer, 4, "%d/", j);
829                          string departureTime = string(buffer) + depTimeGen + string(":00");
830                          string arrivalTime;
831                          if (!arrivalWeekdayNeedsIncrement) {
832                              arrivalTime   = string(buffer) + arrTimeGen + string(":00");
833                          }
834                          if (arrivalWeekdayNeedsIncrement && i != 6 ) {
835                              snprintf(buffer, 4, "%d/", j+1);
836                              arrivalTime   = string(buffer) + arrTimeGen + string(":00");
837                          }
838                          if (arrivalWeekdayNeedsIncrement && i == 6 ) {
839                              snprintf(buffer, 4, "%d/", 0);
840                              arrivalTime   = string(buffer) + arrTimeGen  + string(":00");
841                          }
842                          SG_LOG(SG_AI, SG_ALERT, "Adding flight " << callsign       << " "
843                                                       << fltrules       << " "
844                                                       <<  departurePort << " "
845                                                       <<  arrivalPort   << " "
846                                                       <<  cruiseAlt     << " "
847                                                       <<  departureTime << " "
848                                                       <<  arrivalTime   << " "
849                                                       << repeat        << " " 
850                                                       <<  requiredAircraft);
851
852                          flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
853                                                                  fltrules,
854                                                                  departurePort,
855                                                                  arrivalPort,
856                                                                  cruiseAlt,
857                                                                  departureTime,
858                                                                  arrivalTime,
859                                                                  repeat,
860                                                                  requiredAircraft));
861                     }
862                 }
863              }
864          }
865
866     }
867     //exit(1);
868 }
869
870
871 void FGTrafficManager::Tokenize(const string& str,
872                       vector<string>& tokens,
873                       const string& delimiters)
874 {
875     // Skip delimiters at beginning.
876     string::size_type lastPos = str.find_first_not_of(delimiters, 0);
877     // Find first "non-delimiter".
878     string::size_type pos     = str.find_first_of(delimiters, lastPos);
879
880     while (string::npos != pos || string::npos != lastPos)
881     {
882         // Found a token, add it to the vector.
883         tokens.push_back(str.substr(lastPos, pos - lastPos));
884         // Skip delimiters.  Note the "not_of"
885         lastPos = str.find_first_not_of(delimiters, pos);
886         // Find next "non-delimiter"
887         pos = str.find_first_of(delimiters, lastPos);
888     }
889 }
890
891