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 *****************************************************************************/
56 #include <simgear/compiler.h>
57 #include <simgear/math/polar3d.hxx>
58 #include <simgear/math/sg_geodesy.hxx>
59 #include <simgear/misc/sg_path.hxx>
60 #include <simgear/props/props.hxx>
61 #include <simgear/route/waypoint.hxx>
62 #include <simgear/structure/subsystem_mgr.hxx>
63 #include <simgear/xml/easyxml.hxx>
65 #include <AIModel/AIAircraft.hxx>
66 #include <AIModel/AIFlightPlan.hxx>
67 #include <AIModel/AIBase.hxx>
68 #include <Airports/simple.hxx>
69 #include <Main/fg_init.hxx>
73 #include "TrafficMgr.hxx"
77 /******************************************************************************
79 *****************************************************************************/
80 FGTrafficManager::FGTrafficManager()
87 FGTrafficManager:: ~FGTrafficManager()
89 for (ScheduleVectorIterator sched = scheduledAircraft.begin(); sched != scheduledAircraft.end(); sched++)
93 scheduledAircraft.clear();
98 void FGTrafficManager::init()
101 ulDirEnt* dent, *dent2;
102 SGPath aircraftDir = globals->get_fg_root();
104 SGPath path = aircraftDir;
106 aircraftDir.append("AI/Traffic");
107 if ((d = ulOpenDir(aircraftDir.c_str())) != NULL)
109 while((dent = ulReadDir(d)) != NULL) {
110 if (string(dent->d_name) != string(".") &&
111 string(dent->d_name) != string("..") &&
114 SGPath currACDir = aircraftDir;
115 currACDir.append(dent->d_name);
116 if ((d2 = ulOpenDir(currACDir.c_str())) == NULL)
118 while ((dent2 = ulReadDir(d2)) != NULL) {
119 SGPath currFile = currACDir;
120 currFile.append(dent2->d_name);
121 if (currFile.extension() == string("xml"))
123 SGPath currFile = currACDir;
124 currFile.append(dent2->d_name);
125 SG_LOG(SG_GENERAL, SG_INFO, "Scanning " << currFile.str() << " for traffic");
126 readXML(currFile.str(),*this);
134 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
136 currAircraft = scheduledAircraft.begin();
137 currAircraftClosest = scheduledAircraft.begin();
140 void FGTrafficManager::update(double /*dt*/)
143 time_t now = time(NULL) + fgGetLong("/sim/time/warp");
144 if (scheduledAircraft.size() == 0) {
147 if(currAircraft == scheduledAircraft.end())
149 currAircraft = scheduledAircraft.begin();
151 if (!((*currAircraft)->update(now)))
153 // NOTE: With traffic manager II, this statement below is no longer true
154 // after proper initialization, we shouldnt get here.
155 // But let's make sure
156 //SG_LOG( SG_GENERAL, SG_ALERT, "Failed to update aircraft schedule in traffic manager");
161 void FGTrafficManager::release(int id)
163 releaseList.push_back(id);
166 bool FGTrafficManager::isReleased(int id)
168 IdListIterator i = releaseList.begin();
169 while (i != releaseList.end())
173 releaseList.erase(i);
181 void FGTrafficManager::readTimeTableFromFile(SGPath infileName)
198 vector <string> tokens, depTime,arrTime;
199 vector <string>::iterator it;
200 ifstream infile(infileName.str().c_str());
202 infile.getline(buffer, 256);
206 //cerr << "Read line : " << buffer << endl;
207 buffString = string(buffer);
209 Tokenize(buffString, tokens, " \t");
210 //for (it = tokens.begin(); it != tokens.end(); it++) {
211 // cerr << "Tokens: " << *(it) << endl;
214 if (!tokens.empty()) {
215 if (tokens[0] == string("AC")) {
216 if (tokens.size() != 13) {
217 SG_LOG(SG_GENERAL, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
222 homePort = tokens[1];
223 registration = tokens[2];
224 if (tokens[11] == string("false")) {
231 flightReq = tokens[3] + tokens[5];
232 m_class = tokens[10];
233 FlightType = tokens[9];
234 radius = atof(tokens[8].c_str());
235 offset = atof(tokens[7].c_str());;
236 //cerr << "Found AC string " << model << " " << livery << " " << homePort << " "
237 // << registration << " " << flightReq << " " << isHeavy << " " << acType << " " << airline << " " << m_class
238 // << " " << FlightType << " " << radius << " " << offset << endl;
239 scheduledAircraft.push_back(new FGAISchedule(model,
252 if (tokens[0] == string("FLIGHT")) {
253 //cerr << "Found flight " << buffString << " size is : " << tokens.size() << endl;
254 if (tokens.size() != 10) {
255 SG_LOG(SG_GENERAL, SG_ALERT, "Error parsing traffic file " << infileName.str() << " at " << buffString);
258 string callsign = tokens[1];
259 string fltrules = tokens[2];
260 string weekdays = tokens[3];
261 string departurePort = tokens[5];
262 string arrivalPort = tokens[7];
263 int cruiseAlt = atoi(tokens[8].c_str());
264 string depTimeGen = tokens[4];
265 string arrTimeGen = tokens[6];
266 string repeat = "WEEK";
267 string requiredAircraft = tokens[9];
269 if (weekdays.size() != 7) {
270 cerr << "Found misconfigured weekdays string" << weekdays << endl;
275 Tokenize(depTimeGen, depTime, ":");
276 Tokenize(arrTimeGen, arrTime, ":");
277 double dep = atof(depTime[0].c_str()) + (atof(depTime[1].c_str()) / 60.0);
278 double arr = atof(arrTime[0].c_str()) + (atof(arrTime[1].c_str()) / 60.0);
279 //cerr << "Using " << dep << " " << arr << endl;
280 bool arrivalWeekdayNeedsIncrement = false;
282 arrivalWeekdayNeedsIncrement = true;
284 for (int i = 0; i < 7; i++) {
285 if (weekdays[i] != '.') {
287 snprintf(buffer, 4, "%d/", i);
288 string departureTime = string(buffer) + depTimeGen + string(":00");
290 if (!arrivalWeekdayNeedsIncrement) {
291 arrivalTime = string(buffer) + arrTimeGen + string(":00");
293 if (arrivalWeekdayNeedsIncrement && i != 6 ) {
294 snprintf(buffer, 4, "%d/", i+1);
295 arrivalTime = string(buffer) + arrTimeGen + string(":00");
297 if (arrivalWeekdayNeedsIncrement && i == 6 ) {
298 snprintf(buffer, 4, "%d/", 0);
299 arrivalTime = string(buffer) + arrTimeGen + string(":00");
301 cerr << "Adding flight: " << callsign << " "
303 << departurePort << " "
304 << arrivalPort << " "
306 << departureTime << " "
307 << arrivalTime << " "
309 << requiredAircraft << endl;
311 flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
330 void FGTrafficManager::Tokenize(const string& str,
331 vector<string>& tokens,
332 const string& delimiters)
334 // Skip delimiters at beginning.
335 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
336 // Find first "non-delimiter".
337 string::size_type pos = str.find_first_of(delimiters, lastPos);
339 while (string::npos != pos || string::npos != lastPos)
341 // Found a token, add it to the vector.
342 tokens.push_back(str.substr(lastPos, pos - lastPos));
343 // Skip delimiters. Note the "not_of"
344 lastPos = str.find_first_not_of(delimiters, pos);
345 // Find next "non-delimiter"
346 pos = str.find_first_of(delimiters, lastPos);
351 void FGTrafficManager::startXML () {
352 //cout << "Start XML" << endl;
353 requiredAircraft = "";
357 void FGTrafficManager::endXML () {
358 //cout << "End XML" << endl;
361 void FGTrafficManager::startElement (const char * name, const XMLAttributes &atts) {
363 //cout << "Start element " << name << endl;
364 //FGTrafficManager temp;
365 //for (int i = 0; i < atts.size(); i++)
366 // if (string(atts.getName(i)) == string("include"))
367 attval = atts.getValue("include");
370 //cout << "including " << attval << endl;
372 globals->get_fg_root();
373 path.append("/Traffic/");
375 readXML(path.str(), *this);
377 elementValueStack.push_back( "" );
378 // cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
381 void FGTrafficManager::endElement (const char * name) {
382 //cout << "End element " << name << endl;
383 string element(name);
384 string value = elementValueStack.back();
385 elementValueStack.pop_back();
387 if (element == string("model"))
389 else if (element == string("livery"))
391 else if (element == string("home-port"))
393 else if (element == string("registration"))
394 registration = value;
395 else if (element == string("airline"))
397 else if (element == string("actype"))
399 else if (element == string("required-aircraft"))
400 requiredAircraft = value;
401 else if (element == string("flighttype"))
403 else if (element == string("radius"))
404 radius = atoi(value.c_str());
405 else if (element == string("offset"))
406 offset = atoi(value.c_str());
407 else if (element == string("performance-class"))
409 else if (element == string("heavy"))
411 if(value == string("true"))
416 else if (element == string("callsign"))
418 else if (element == string("fltrules"))
420 else if (element == string("port"))
422 else if (element == string("time"))
424 else if (element == string("departure"))
426 departurePort = port;
427 departureTime = timeString;
429 else if (element == string("cruise-alt"))
430 cruiseAlt = atoi(value.c_str());
431 else if (element == string("arrival"))
434 arrivalTime = timeString;
436 else if (element == string("repeat"))
438 else if (element == string("flight"))
440 // We have loaded and parsed all the information belonging to this flight
441 // so we temporarily store it.
442 //cerr << "Pusing back flight " << callsign << endl;
443 //cerr << callsign << " " << fltrules << " "<< departurePort << " " << arrivalPort << " "
444 // << cruiseAlt << " " << departureTime<< " "<< arrivalTime << " " << repeat << endl;
446 //Prioritize aircraft
447 string apt = fgGetString("/sim/presets/airport-id");
448 //cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl;
449 //if (departurePort == apt) score++;
450 //flights.push_back(new FGScheduledFlight(callsign,
458 if (requiredAircraft == "") {
460 snprintf(buffer, 16, "%d", acCounter);
461 requiredAircraft = buffer;
463 SG_LOG(SG_GENERAL, SG_DEBUG, "Adding flight: " << callsign << " "
465 << departurePort << " "
466 << arrivalPort << " "
468 << departureTime << " "
469 << arrivalTime << " "
471 << requiredAircraft);
473 flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
482 requiredAircraft = "";
484 else if (element == string("aircraft"))
486 int proportion = (int) (fgGetDouble("/sim/traffic-manager/proportion") * 100);
487 int randval = rand() & 100;
488 if (randval < proportion) {
489 //scheduledAircraft.push_back(new FGAISchedule(mdl,
501 if (requiredAircraft == "") {
503 snprintf(buffer, 16, "%d", acCounter);
504 requiredAircraft = buffer;
506 if (homePort == "") {
507 homePort = departurePort;
509 scheduledAircraft.push_back(new FGAISchedule(mdl,
522 // while(flights.begin() != flights.end()) {
523 // flights.pop_back();
527 requiredAircraft = "";
529 //for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++)
534 SG_LOG( SG_GENERAL, SG_BULK, "Reading aircraft : "
536 << " with prioritization score "
542 void FGTrafficManager::data (const char * s, int len) {
543 string token = string(s,len);
544 //cout << "Character data " << string(s,len) << endl;
545 elementValueStack.back() += token;
548 void FGTrafficManager::pi (const char * target, const char * data) {
549 //cout << "Processing instruction " << target << ' ' << data << endl;
552 void FGTrafficManager::warning (const char * message, int line, int column) {
553 SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
556 void FGTrafficManager::error (const char * message, int line, int column) {
557 SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');