]> git.mxchange.org Git - flightgear.git/blob - src/Traffic/TrafficMgr.cxx
GUI ‘restore defaults’ support.
[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     // TorstenD: don't start the traffic manager before the FDM is initialized
496     // The FDM needs the scenery loaded and will wait for our spawned AIModels PagedLOD Nodes 
497     // to appear if they are close (less than 1000m) to our position
498     if( !fgGetBool("/sim/signals/fdm-initialized") )
499       return;
500
501     assert(!doingInit);
502     simgear::SGTerraSync* terraSync = static_cast<simgear::SGTerraSync*>(globals->get_subsystem("terrasync"));
503     bool doDataSync = fgGetBool("/sim/terrasync/ai-data-enabled");
504     if (doDataSync && terraSync) {
505         if (!trafficSyncRequested) {
506             SG_LOG(SG_AI, SG_INFO, "Sync of AI traffic via TerraSync enabled");
507             terraSync->scheduleDataDir("AI/Traffic");
508             trafficSyncRequested = true;
509         }
510         
511         if (terraSync->isDataDirPending("AI/Traffic")) {
512             return; // remain in the init state
513         }
514         
515         SG_LOG(SG_AI, SG_INFO, "Traffic files sync complete");
516     }
517     
518     doingInit = true;
519     if (string(fgGetString("/sim/traffic-manager/datafile")).empty()) {
520         PathList dirs = globals->get_data_paths("AI/Traffic");
521         
522         // temporary flag to restrict loading while traffic data is found
523         // through terrasync /and/ fgdata. Ultimatley we *do* want to be able to
524         // overlay sources.
525         
526         if (dirs.size() > 1) {
527             SGPath p = dirs.back();
528             if (simgear::strutils::starts_with(p.str(), globals->get_fg_root())) {
529                 dirs.pop_back();
530             }
531         }
532         
533         if (dirs.empty()) {
534             doingInit = false;
535             return;
536         }
537         
538         scheduleParser.reset(new ScheduleParseThread(this));
539         scheduleParser->setTrafficDirs(dirs);
540         scheduleParser->start();
541     } else {
542         fgSetBool("/sim/traffic-manager/heuristics", false);
543         SGPath path = string(fgGetString("/sim/traffic-manager/datafile"));
544         string ext = path.extension();
545         if (path.extension() == "xml") {
546             if (path.exists()) {
547                 // use a SchedulerParser to parse, but run it in this thread,
548                 // i.e don't start it
549                 ScheduleParseThread parser(this);
550                 readXML(path.str(), parser);
551             }
552         } else if (path.extension() == "conf") {
553             if (path.exists()) {
554                 readTimeTableFromFile(path);
555             }
556         } else {
557              SG_LOG(SG_AI, SG_ALERT,
558                                "Unknown data format " << path.str()
559                                 << " for traffic");
560         }
561         //exit(1);
562     }
563 }
564
565
566
567 void FGTrafficManager::finishInit()
568 {
569     assert(doingInit);
570     SG_LOG(SG_AI, SG_INFO, "finishing AI-Traffic init");
571     loadHeuristics();
572     
573     // Do sorting and scoring separately, to take advantage of the "homeport" variable
574     BOOST_FOREACH(FGAISchedule* schedule, scheduledAircraft) {
575         schedule->setScore();
576     }
577     
578     sort(scheduledAircraft.begin(), scheduledAircraft.end(),
579          compareSchedules);
580     currAircraft = scheduledAircraft.begin();
581     currAircraftClosest = scheduledAircraft.begin();
582     
583     doingInit = false;
584     inited = true;
585 }
586
587 void FGTrafficManager::loadHeuristics()
588 {
589     if (!fgGetBool("/sim/traffic-manager/heuristics")) {
590         return;
591     }
592   
593     HeuristicMap heurMap;
594     //cerr << "Processing Heuristics" << endl;
595     // Load the heuristics data
596     SGPath cacheData(globals->get_fg_home());
597     cacheData.append("ai");
598     string airport = fgGetString("/sim/presets/airport-id");
599     if ((airport) != "") {
600       char buffer[128];
601       ::snprintf(buffer, 128, "%c/%c/%c/",
602                  airport[0], airport[1], airport[2]);
603       cacheData.append(buffer);
604       cacheData.append(airport + "-cache.txt");
605       string revisionStr;
606       if (cacheData.exists()) {
607         std::ifstream data(cacheData.c_str());
608         data >> revisionStr;
609         if (revisionStr != "[TrafficManagerCachedata:ref:2011:09:04]") {
610           SG_LOG(SG_AI, SG_ALERT,"Traffic Manager Warning: discarding outdated cachefile " <<
611                  cacheData.c_str() << " for Airport " << airport);
612         } else {
613           while (1) {
614             Heuristic h; // = new Heuristic;
615             data >> h.registration >> h.runCount >> h.hits >> h.lastRun;
616             if (data.eof())
617               break;
618             HeuristicMapIterator itr = heurMap.find(h.registration);
619             if (itr != heurMap.end()) {
620               SG_LOG(SG_AI, SG_WARN,"Traffic Manager Warning: found duplicate tailnumber " <<
621                      h.registration << " for AI aircraft");
622             } else {
623               heurMap[h.registration] = h;
624             }
625           }
626         }
627       }
628     } 
629     
630   for(currAircraft = scheduledAircraft.begin(); currAircraft != scheduledAircraft.end(); ++currAircraft) {
631         const string& registration = (*currAircraft)->getRegistration();
632         HeuristicMapIterator itr = heurMap.find(registration);
633         if (itr != heurMap.end()) {
634             (*currAircraft)->setrunCount(itr->second.runCount);
635             (*currAircraft)->setHits(itr->second.hits);
636             (*currAircraft)->setLastUsed(itr->second.lastRun);
637         }
638     }
639 }
640
641 bool FGTrafficManager::metarReady(double dt)
642 {
643     // wait for valid METAR (when realWX is enabled only), since we need
644     // to know the active runway
645     if (metarValid || !realWxEnabled)
646     {
647         waitingMetarTime = 0.0;
648         return true;
649     }
650
651     // METAR timeout: when running offline, remote server is down etc
652     if (waitingMetarStation != fgGetString("/environment/metar/station-id"))
653     {
654         // station has changed: wait for reply, restart timeout
655         waitingMetarTime = 0.0;
656         waitingMetarStation = fgGetString("/environment/metar/station-id");
657         return false;
658     }
659
660     // timeout elapsed (10 seconds)?
661     if (waitingMetarTime > 20.0)
662     {
663         return true;
664     }
665
666     waitingMetarTime += dt;
667     return false;
668 }
669
670 void FGTrafficManager::update(double dt)
671 {
672     if (!enabled)
673     {
674         if (inited || doingInit)
675             shutdown();
676         return;
677     }
678
679     if (!metarReady(dt))
680         return;
681
682     if (!aiEnabled)
683     {
684         // traffic depends on AI module
685         aiEnabled = true;
686     }
687
688     if (!inited) {
689         if (!doingInit) {
690             init();
691         }
692                 
693         if (!doingInit || !scheduleParser->isFinished()) {
694           return;
695         }
696       
697         finishInit();
698     }
699         
700     time_t now = time(NULL) + fgGetLong("/sim/time/warp");
701     if (scheduledAircraft.empty()) {
702         return;
703     }
704
705     SGVec3d userCart = globals->get_aircraft_position_cart();
706
707     if (currAircraft == scheduledAircraft.end()) {
708         currAircraft = scheduledAircraft.begin();
709     }
710
711     //cerr << "Processing << " << (*currAircraft)->getRegistration() << " with score " << (*currAircraft)->getScore() << endl;
712     if ((*currAircraft)->update(now, userCart)) {
713         // schedule is done - process another aircraft in next iteration
714         currAircraft++;
715     }
716 }
717
718 void FGTrafficManager::readTimeTableFromFile(SGPath infileName)
719 {
720     string model;
721     string livery;
722     string homePort;
723     string registration;
724     string flightReq;
725     bool   isHeavy;
726     string acType;
727     string airline;
728     string m_class;
729     string FlightType;
730     double radius;
731     double offset;
732
733     char buffer[256];
734     string buffString;
735     vector <string> tokens, depTime,arrTime;
736     vector <string>::iterator it;
737     std::ifstream infile(infileName.str().c_str());
738     while (1) {
739          infile.getline(buffer, 256);
740          if (infile.eof()) {
741              break;
742          }
743          //cerr << "Read line : " << buffer << endl;
744          buffString = string(buffer);
745          tokens.clear();
746          Tokenize(buffString, tokens, " \t");
747          //for (it = tokens.begin(); it != tokens.end(); it++) {
748          //    cerr << "Tokens: " << *(it) << endl;
749          //}
750          //cerr << endl;
751          if (!tokens.empty()) {
752              if (tokens[0] == string("AC")) {
753                  if (tokens.size() != 13) {
754                      throw sg_io_exception("Error parsing traffic file @ " + buffString, sg_location(infileName.str()));
755                  }
756                  
757                  
758                  model          = tokens[12];
759                  livery         = tokens[6];
760                  homePort       = tokens[1];
761                  registration   = tokens[2];
762                  if (tokens[11] == string("false")) {
763                      isHeavy = false;
764                  } else {
765                      isHeavy = true;
766                  }
767                  acType         = tokens[4];
768                  airline        = tokens[5];
769                  flightReq      = tokens[3] + tokens[5];
770                  m_class        = tokens[10];
771                  FlightType     = tokens[9];
772                  radius         = atof(tokens[8].c_str());
773                  offset         = atof(tokens[7].c_str());;
774                  
775                  if (!FGAISchedule::validModelPath(model)) {
776                      SG_LOG(SG_AI, SG_WARN, "TrafficMgr: Missing model path:" <<
777                             model << " from " << infileName.str());
778                  } else {
779                  
780                  SG_LOG(SG_AI, SG_INFO, "Adding Aircraft" << model << " " << livery << " " << homePort << " "
781                                                                 << registration << " " << flightReq << " " << isHeavy 
782                                                                 << " " << acType << " " << airline << " " << m_class 
783                                                                 << " " << FlightType << " " << radius << " " << offset);
784                  scheduledAircraft.push_back(new FGAISchedule(model, 
785                                                               livery, 
786                                                               homePort,
787                                                               registration, 
788                                                               flightReq,
789                                                               isHeavy,
790                                                               acType, 
791                                                               airline, 
792                                                               m_class, 
793                                                               FlightType,
794                                                               radius,
795                                                               offset));
796                  } // of valid model path
797              }
798              if (tokens[0] == string("FLIGHT")) {
799                  //cerr << "Found flight " << buffString << " size is : " << tokens.size() << endl;
800                  if (tokens.size() != 10) {
801                      SG_LOG(SG_AI, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
802                      exit(1);
803                  }
804                  string callsign = tokens[1];
805                  string fltrules = tokens[2];
806                  string weekdays = tokens[3];
807                  string departurePort = tokens[5];
808                  string arrivalPort   = tokens[7];
809                  int    cruiseAlt     = atoi(tokens[8].c_str());
810                  string depTimeGen    = tokens[4];
811                  string arrTimeGen    = tokens[6];
812                  string repeat        = "WEEK";
813                  string requiredAircraft = tokens[9];
814
815                  if (weekdays.size() != 7) {
816                      SG_LOG(SG_AI, SG_ALERT, "Found misconfigured weekdays string" << weekdays);
817                      exit(1);
818                  }
819                  depTime.clear();
820                  arrTime.clear();
821                  Tokenize(depTimeGen, depTime, ":");
822                  Tokenize(arrTimeGen, arrTime, ":");
823                  double dep = atof(depTime[0].c_str()) + (atof(depTime[1].c_str()) / 60.0);
824                  double arr = atof(arrTime[0].c_str()) + (atof(arrTime[1].c_str()) / 60.0);
825                  //cerr << "Using " << dep << " " << arr << endl;
826                  bool arrivalWeekdayNeedsIncrement = false;
827                  if (arr < dep) {
828                        arrivalWeekdayNeedsIncrement = true;
829                  }
830                  for (int i = 0; i < 7; i++) {
831                      int j = i+1;
832                      if (weekdays[i] != '.') {
833                          char buffer[4];
834                          snprintf(buffer, 4, "%d/", j);
835                          string departureTime = string(buffer) + depTimeGen + string(":00");
836                          string arrivalTime;
837                          if (!arrivalWeekdayNeedsIncrement) {
838                              arrivalTime   = string(buffer) + arrTimeGen + string(":00");
839                          }
840                          if (arrivalWeekdayNeedsIncrement && i != 6 ) {
841                              snprintf(buffer, 4, "%d/", j+1);
842                              arrivalTime   = string(buffer) + arrTimeGen + string(":00");
843                          }
844                          if (arrivalWeekdayNeedsIncrement && i == 6 ) {
845                              snprintf(buffer, 4, "%d/", 0);
846                              arrivalTime   = string(buffer) + arrTimeGen  + string(":00");
847                          }
848                          SG_LOG(SG_AI, SG_ALERT, "Adding flight " << callsign       << " "
849                                                       << fltrules       << " "
850                                                       <<  departurePort << " "
851                                                       <<  arrivalPort   << " "
852                                                       <<  cruiseAlt     << " "
853                                                       <<  departureTime << " "
854                                                       <<  arrivalTime   << " "
855                                                       << repeat        << " " 
856                                                       <<  requiredAircraft);
857
858                          flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
859                                                                  fltrules,
860                                                                  departurePort,
861                                                                  arrivalPort,
862                                                                  cruiseAlt,
863                                                                  departureTime,
864                                                                  arrivalTime,
865                                                                  repeat,
866                                                                  requiredAircraft));
867                     }
868                 }
869              }
870          }
871
872     }
873     //exit(1);
874 }
875
876
877 void FGTrafficManager::Tokenize(const string& str,
878                       vector<string>& tokens,
879                       const string& delimiters)
880 {
881     // Skip delimiters at beginning.
882     string::size_type lastPos = str.find_first_not_of(delimiters, 0);
883     // Find first "non-delimiter".
884     string::size_type pos     = str.find_first_of(delimiters, lastPos);
885
886     while (string::npos != pos || string::npos != lastPos)
887     {
888         // Found a token, add it to the vector.
889         tokens.push_back(str.substr(lastPos, pos - lastPos));
890         // Skip delimiters.  Note the "not_of"
891         lastPos = str.find_first_not_of(delimiters, pos);
892         // Find next "non-delimiter"
893         pos = str.find_first_of(delimiters, lastPos);
894     }
895 }
896
897