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