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