1 /******************************************************************************
3 * Written by Durk Talsma, started May 5, 2004.
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.
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.
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.
20 **************************************************************************/
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.
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
33 * 4) Each aircraft leaves at exactly the departure time.
34 * 5) Each aircraft arrives at exactly the specified arrival time.
37 *****************************************************************************/
55 #include <simgear/compiler.h>
56 #include <simgear/misc/sg_path.hxx>
57 #include <simgear/props/props.hxx>
58 #include <simgear/route/waypoint.hxx>
59 #include <simgear/structure/subsystem_mgr.hxx>
60 #include <simgear/xml/easyxml.hxx>
62 #include <AIModel/AIAircraft.hxx>
63 #include <AIModel/AIFlightPlan.hxx>
64 #include <AIModel/AIBase.hxx>
65 #include <Airports/simple.hxx>
66 #include <Main/fg_init.hxx>
70 #include "TrafficMgr.hxx"
74 /******************************************************************************
76 *****************************************************************************/
77 FGTrafficManager::FGTrafficManager()
84 FGTrafficManager:: ~FGTrafficManager()
86 for (ScheduleVectorIterator sched = scheduledAircraft.begin(); sched != scheduledAircraft.end(); sched++)
90 scheduledAircraft.clear();
95 void FGTrafficManager::init()
98 ulDirEnt* dent, *dent2;
99 SGPath aircraftDir = globals->get_fg_root();
101 SGPath path = aircraftDir;
103 aircraftDir.append("AI/Traffic");
104 if ((d = ulOpenDir(aircraftDir.c_str())) != NULL)
106 while((dent = ulReadDir(d)) != NULL) {
107 if (string(dent->d_name) != string(".") &&
108 string(dent->d_name) != string("..") &&
111 SGPath currACDir = aircraftDir;
112 currACDir.append(dent->d_name);
113 if ((d2 = ulOpenDir(currACDir.c_str())) == NULL)
115 while ((dent2 = ulReadDir(d2)) != NULL) {
116 SGPath currFile = currACDir;
117 currFile.append(dent2->d_name);
118 if (currFile.extension() == string("xml"))
120 SGPath currFile = currACDir;
121 currFile.append(dent2->d_name);
122 SG_LOG(SG_GENERAL, SG_INFO, "Scanning " << currFile.str() << " for traffic");
123 readXML(currFile.str(),*this);
132 currAircraft = scheduledAircraft.begin();
133 currAircraftClosest = scheduledAircraft.begin();
136 void FGTrafficManager::update(double /*dt*/)
139 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
140 if (scheduledAircraft.size() == 0) {
143 if(currAircraft == scheduledAircraft.end())
145 currAircraft = scheduledAircraft.begin();
147 if (!((*currAircraft)->update(now)))
149 // NOTE: With traffic manager II, this statement below is no longer true
150 // after proper initialization, we shouldnt get here.
151 // But let's make sure
152 //SG_LOG( SG_GENERAL, SG_ALERT, "Failed to update aircraft schedule in traffic manager");
157 void FGTrafficManager::release(int id)
159 releaseList.push_back(id);
162 bool FGTrafficManager::isReleased(int id)
164 IdListIterator i = releaseList.begin();
165 while (i != releaseList.end())
169 releaseList.erase(i);
177 void FGTrafficManager::readTimeTableFromFile(SGPath infileName)
194 vector <string> tokens, depTime,arrTime;
195 vector <string>::iterator it;
196 ifstream infile(infileName.str().c_str());
198 infile.getline(buffer, 256);
202 //cerr << "Read line : " << buffer << endl;
203 buffString = string(buffer);
205 Tokenize(buffString, tokens, " \t");
206 //for (it = tokens.begin(); it != tokens.end(); it++) {
207 // cerr << "Tokens: " << *(it) << endl;
210 if (!tokens.empty()) {
211 if (tokens[0] == string("AC")) {
212 if (tokens.size() != 13) {
213 SG_LOG(SG_GENERAL, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
218 homePort = tokens[1];
219 registration = tokens[2];
220 if (tokens[11] == string("false")) {
227 flightReq = tokens[3] + tokens[5];
228 m_class = tokens[10];
229 FlightType = tokens[9];
230 radius = atof(tokens[8].c_str());
231 offset = atof(tokens[7].c_str());;
232 //cerr << "Found AC string " << model << " " << livery << " " << homePort << " "
233 // << registration << " " << flightReq << " " << isHeavy << " " << acType << " " << airline << " " << m_class
234 // << " " << FlightType << " " << radius << " " << offset << endl;
235 scheduledAircraft.push_back(new FGAISchedule(model,
248 if (tokens[0] == string("FLIGHT")) {
249 //cerr << "Found flight " << buffString << " size is : " << tokens.size() << endl;
250 if (tokens.size() != 10) {
251 SG_LOG(SG_GENERAL, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
254 string callsign = tokens[1];
255 string fltrules = tokens[2];
256 string weekdays = tokens[3];
257 string departurePort = tokens[5];
258 string arrivalPort = tokens[7];
259 int cruiseAlt = atoi(tokens[8].c_str());
260 string depTimeGen = tokens[4];
261 string arrTimeGen = tokens[6];
262 string repeat = "WEEK";
263 string requiredAircraft = tokens[9];
265 if (weekdays.size() != 7) {
266 cerr << "Found misconfigured weekdays string" << weekdays << endl;
271 Tokenize(depTimeGen, depTime, ":");
272 Tokenize(arrTimeGen, arrTime, ":");
273 double dep = atof(depTime[0].c_str()) + (atof(depTime[1].c_str()) / 60.0);
274 double arr = atof(arrTime[0].c_str()) + (atof(arrTime[1].c_str()) / 60.0);
275 //cerr << "Using " << dep << " " << arr << endl;
276 bool arrivalWeekdayNeedsIncrement = false;
278 arrivalWeekdayNeedsIncrement = true;
280 for (int i = 0; i < 7; i++) {
281 if (weekdays[i] != '.') {
283 snprintf(buffer, 4, "%d/", i);
284 string departureTime = string(buffer) + depTimeGen + string(":00");
286 if (!arrivalWeekdayNeedsIncrement) {
287 arrivalTime = string(buffer) + arrTimeGen + string(":00");
289 if (arrivalWeekdayNeedsIncrement && i != 6 ) {
290 snprintf(buffer, 4, "%d/", i+1);
291 arrivalTime = string(buffer) + arrTimeGen + string(":00");
293 if (arrivalWeekdayNeedsIncrement && i == 6 ) {
294 snprintf(buffer, 4, "%d/", 0);
295 arrivalTime = string(buffer) + arrTimeGen + string(":00");
297 cerr << "Adding flight: " << callsign << " "
299 << departurePort << " "
300 << arrivalPort << " "
302 << departureTime << " "
303 << arrivalTime << " "
305 << requiredAircraft << endl;
307 flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
326 void FGTrafficManager::Tokenize(const string& str,
327 vector<string>& tokens,
328 const string& delimiters)
330 // Skip delimiters at beginning.
331 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
332 // Find first "non-delimiter".
333 string::size_type pos = str.find_first_of(delimiters, lastPos);
335 while (string::npos != pos || string::npos != lastPos)
337 // Found a token, add it to the vector.
338 tokens.push_back(str.substr(lastPos, pos - lastPos));
339 // Skip delimiters. Note the "not_of"
340 lastPos = str.find_first_not_of(delimiters, pos);
341 // Find next "non-delimiter"
342 pos = str.find_first_of(delimiters, lastPos);
347 void FGTrafficManager::startXML () {
348 //cout << "Start XML" << endl;
349 requiredAircraft = "";
353 void FGTrafficManager::endXML () {
354 //cout << "End XML" << endl;
357 void FGTrafficManager::startElement (const char * name, const XMLAttributes &atts) {
359 //cout << "Start element " << name << endl;
360 //FGTrafficManager temp;
361 //for (int i = 0; i < atts.size(); i++)
362 // if (string(atts.getName(i)) == string("include"))
363 attval = atts.getValue("include");
366 //cout << "including " << attval << endl;
368 globals->get_fg_root();
369 path.append("/Traffic/");
371 readXML(path.str(), *this);
373 elementValueStack.push_back( "" );
374 // cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
377 void FGTrafficManager::endElement (const char * name) {
378 //cout << "End element " << name << endl;
379 string element(name);
380 string value = elementValueStack.back();
381 elementValueStack.pop_back();
383 if (element == string("model"))
385 else if (element == string("livery"))
387 else if (element == string("home-port"))
389 else if (element == string("registration"))
390 registration = value;
391 else if (element == string("airline"))
393 else if (element == string("actype"))
395 else if (element == string("required-aircraft"))
396 requiredAircraft = value;
397 else if (element == string("flighttype"))
399 else if (element == string("radius"))
400 radius = atoi(value.c_str());
401 else if (element == string("offset"))
402 offset = atoi(value.c_str());
403 else if (element == string("performance-class"))
405 else if (element == string("heavy"))
407 if(value == string("true"))
412 else if (element == string("callsign"))
414 else if (element == string("fltrules"))
416 else if (element == string("port"))
418 else if (element == string("time"))
420 else if (element == string("departure"))
422 departurePort = port;
423 departureTime = timeString;
425 else if (element == string("cruise-alt"))
426 cruiseAlt = atoi(value.c_str());
427 else if (element == string("arrival"))
430 arrivalTime = timeString;
432 else if (element == string("repeat"))
434 else if (element == string("flight"))
436 // We have loaded and parsed all the information belonging to this flight
437 // so we temporarily store it.
438 //cerr << "Pusing back flight " << callsign << endl;
439 //cerr << callsign << " " << fltrules << " "<< departurePort << " " << arrivalPort << " "
440 // << cruiseAlt << " " << departureTime<< " "<< arrivalTime << " " << repeat << endl;
442 //Prioritize aircraft
443 string apt = fgGetString("/sim/presets/airport-id");
444 //cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl;
445 //if (departurePort == apt) score++;
446 //flights.push_back(new FGScheduledFlight(callsign,
454 if (requiredAircraft == "") {
456 snprintf(buffer, 16, "%d", acCounter);
457 requiredAircraft = buffer;
459 SG_LOG(SG_GENERAL, SG_DEBUG, "Adding flight: " << callsign << " "
461 << departurePort << " "
462 << arrivalPort << " "
464 << departureTime << " "
465 << arrivalTime << " "
467 << requiredAircraft);
469 flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
478 requiredAircraft = "";
480 else if (element == string("aircraft"))
482 int proportion = (int) (fgGetDouble("/sim/traffic-manager/proportion") * 100);
483 int randval = rand() & 100;
484 if (randval < proportion) {
485 //scheduledAircraft.push_back(new FGAISchedule(mdl,
497 if (requiredAircraft == "") {
499 snprintf(buffer, 16, "%d", acCounter);
500 requiredAircraft = buffer;
502 if (homePort == "") {
503 homePort = departurePort;
505 scheduledAircraft.push_back(new FGAISchedule(mdl,
518 // while(flights.begin() != flights.end()) {
519 // flights.pop_back();
523 requiredAircraft = "";
525 //for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++)
530 SG_LOG( SG_GENERAL, SG_BULK, "Reading aircraft : "
532 << " with prioritization score "
538 void FGTrafficManager::data (const char * s, int len) {
539 string token = string(s,len);
540 //cout << "Character data " << string(s,len) << endl;
541 elementValueStack.back() += token;
544 void FGTrafficManager::pi (const char * target, const char * data) {
545 //cout << "Processing instruction " << target << ' ' << data << endl;
548 void FGTrafficManager::warning (const char * message, int line, int column) {
549 SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
552 void FGTrafficManager::error (const char * message, int line, int column) {
553 SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');