Support for multiple data dirs.
This commit is contained in:
parent
fda64d840e
commit
33425c671d
6 changed files with 390 additions and 295 deletions
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
@ -350,9 +351,11 @@ bool FGAIBase::init(bool search_in_AI_path)
|
||||||
osg::ref_ptr<osgDB::ReaderWriter::Options>
|
osg::ref_ptr<osgDB::ReaderWriter::Options>
|
||||||
opt(osg::clone(osgDB::Registry::instance()->getOptions(), osg::CopyOp::SHALLOW_COPY));
|
opt(osg::clone(osgDB::Registry::instance()->getOptions(), osg::CopyOp::SHALLOW_COPY));
|
||||||
|
|
||||||
SGPath ai_path(globals->get_fg_root(), "AI");
|
osgDB::FilePathList& paths(opt->getDatabasePathList());
|
||||||
opt->setDatabasePath(ai_path.str());
|
paths.clear();
|
||||||
|
BOOST_FOREACH(SGPath p, globals->get_data_paths("AI")) {
|
||||||
|
paths.push_back(p.str());
|
||||||
|
}
|
||||||
f = osgDB::findDataFile(model_path, opt.get());
|
f = osgDB::findDataFile(model_path, opt.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -252,6 +252,54 @@ void FGGlobals::set_fg_home (const std::string &home) {
|
||||||
fg_home = tmp.realpath();
|
fg_home = tmp.realpath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PathList FGGlobals::get_data_paths() const
|
||||||
|
{
|
||||||
|
PathList r(additional_data_paths);
|
||||||
|
r.push_back(SGPath(fg_root));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathList FGGlobals::get_data_paths(const std::string& suffix) const
|
||||||
|
{
|
||||||
|
PathList r;
|
||||||
|
BOOST_FOREACH(SGPath p, get_data_paths()) {
|
||||||
|
p.append(suffix);
|
||||||
|
if (p.exists()) {
|
||||||
|
r.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGGlobals::append_data_path(const SGPath& path)
|
||||||
|
{
|
||||||
|
if (!path.exists()) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_WARN, "adding non-existant data path:" << path);
|
||||||
|
}
|
||||||
|
|
||||||
|
additional_data_paths.push_back(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPath FGGlobals::find_data_dir(const std::string& pathSuffix) const
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(SGPath p, additional_data_paths) {
|
||||||
|
p.append(pathSuffix);
|
||||||
|
if (p.exists()) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPath rootPath(fg_root);
|
||||||
|
rootPath.append(pathSuffix);
|
||||||
|
if (rootPath.exists()) {
|
||||||
|
return rootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
SG_LOG(SG_GENERAL, SG_WARN, "dir not found in any data path:" << pathSuffix);
|
||||||
|
return SGPath();
|
||||||
|
}
|
||||||
|
|
||||||
void FGGlobals::append_fg_scenery (const std::string &paths)
|
void FGGlobals::append_fg_scenery (const std::string &paths)
|
||||||
{
|
{
|
||||||
// fg_scenery.clear();
|
// fg_scenery.clear();
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
typedef std::vector<std::string> string_list;
|
typedef std::vector<std::string> string_list;
|
||||||
|
typedef std::vector<SGPath> PathList;
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
|
|
||||||
|
@ -93,6 +94,11 @@ private:
|
||||||
// Root of FlightGear data tree
|
// Root of FlightGear data tree
|
||||||
std::string fg_root;
|
std::string fg_root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* locations to search for (non-scenery) data.
|
||||||
|
*/
|
||||||
|
PathList additional_data_paths;
|
||||||
|
|
||||||
// Users home directory for data
|
// Users home directory for data
|
||||||
std::string fg_home;
|
std::string fg_home;
|
||||||
|
|
||||||
|
@ -178,6 +184,26 @@ public:
|
||||||
inline const std::string &get_fg_root () const { return fg_root; }
|
inline const std::string &get_fg_root () const { return fg_root; }
|
||||||
void set_fg_root (const std::string &root);
|
void set_fg_root (const std::string &root);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of data locations. fg_root is always the final item in the
|
||||||
|
* result.
|
||||||
|
*/
|
||||||
|
PathList get_data_paths() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data locations which contain the file path suffix. Eg pass ing
|
||||||
|
* 'AI/Traffic' to get all data paths which define <path>/AI/Traffic subdir
|
||||||
|
*/
|
||||||
|
PathList get_data_paths(const std::string& suffix) const;
|
||||||
|
|
||||||
|
void append_data_path(const SGPath& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a path suffix (eg 'Textures' or 'AI/Traffic'), find the
|
||||||
|
* first data directory which defines it.
|
||||||
|
*/
|
||||||
|
SGPath find_data_dir(const std::string& pathSuffix) const;
|
||||||
|
|
||||||
inline const std::string &get_fg_home () const { return fg_home; }
|
inline const std::string &get_fg_home () const { return fg_home; }
|
||||||
void set_fg_home (const std::string &home);
|
void set_fg_home (const std::string &home);
|
||||||
|
|
||||||
|
|
|
@ -596,6 +596,16 @@ static naRef f_resolveDataPath(naContext c, naRef me, int argc, naRef* args)
|
||||||
return naStr_fromdata(naNewString(c), const_cast<char*>(pdata), strlen(pdata));
|
return naStr_fromdata(naNewString(c), const_cast<char*>(pdata), strlen(pdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static naRef f_findDataDir(naContext c, naRef me, int argc, naRef* args)
|
||||||
|
{
|
||||||
|
if(argc != 1 || !naIsString(args[0]))
|
||||||
|
naRuntimeError(c, "bad arguments to findDataDir()");
|
||||||
|
|
||||||
|
SGPath p = globals->find_data_dir(naStr_data(args[0]));
|
||||||
|
const char* pdata = p.c_str();
|
||||||
|
return naStr_fromdata(naNewString(c), const_cast<char*>(pdata), strlen(pdata));
|
||||||
|
}
|
||||||
|
|
||||||
class NasalCommand : public SGCommandMgr::Command
|
class NasalCommand : public SGCommandMgr::Command
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -724,6 +734,7 @@ static struct { const char* name; naCFunction func; } funcs[] = {
|
||||||
{ "abort", f_abort },
|
{ "abort", f_abort },
|
||||||
{ "directory", f_directory },
|
{ "directory", f_directory },
|
||||||
{ "resolvepath", f_resolveDataPath },
|
{ "resolvepath", f_resolveDataPath },
|
||||||
|
{ "finddata", f_findDataDir },
|
||||||
{ "parsexml", f_parsexml },
|
{ "parsexml", f_parsexml },
|
||||||
{ "systime", f_systime },
|
{ "systime", f_systime },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
|
|
|
@ -76,13 +76,19 @@ using std::endl;
|
||||||
/**
|
/**
|
||||||
* Thread encapsulating parsing the traffic schedules.
|
* Thread encapsulating parsing the traffic schedules.
|
||||||
*/
|
*/
|
||||||
class ScheduleParseThread : public SGThread
|
class ScheduleParseThread : public SGThread, public XMLVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ScheduleParseThread(FGTrafficManager* traffic) :
|
ScheduleParseThread(FGTrafficManager* traffic) :
|
||||||
_trafficManager(traffic),
|
_trafficManager(traffic),
|
||||||
_isFinished(false),
|
_isFinished(false),
|
||||||
_cancelThread(false)
|
_cancelThread(false),
|
||||||
|
cruiseAlt(0),
|
||||||
|
score(0),
|
||||||
|
acCounter(0),
|
||||||
|
radius(0),
|
||||||
|
offset(0),
|
||||||
|
heavy(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -100,9 +106,9 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTrafficDir(const SGPath& trafficDirPath)
|
void setTrafficDirs(const PathList& dirs)
|
||||||
{
|
{
|
||||||
_trafficDirPath = trafficDirPath;
|
_trafficDirPaths = dirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFinished() const
|
bool isFinished() const
|
||||||
|
@ -113,35 +119,289 @@ public:
|
||||||
|
|
||||||
virtual void run()
|
virtual void run()
|
||||||
{
|
{
|
||||||
SGTimeStamp st;
|
BOOST_FOREACH(SGPath p, _trafficDirPaths) {
|
||||||
st.stamp();
|
parseTrafficDir(p);
|
||||||
|
if (_cancelThread) {
|
||||||
simgear::Dir trafficDir(_trafficDirPath);
|
return;
|
||||||
simgear::PathList d = trafficDir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
|
}
|
||||||
|
|
||||||
BOOST_FOREACH(SGPath p, d) {
|
|
||||||
simgear::Dir d2(p);
|
|
||||||
simgear::PathList trafficFiles = d2.children(simgear::Dir::TYPE_FILE, ".xml");
|
|
||||||
BOOST_FOREACH(SGPath xml, trafficFiles) {
|
|
||||||
_trafficManager->parseSchedule(xml);
|
|
||||||
if (_cancelThread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // of sub-directories in AI/Traffic iteration
|
|
||||||
|
|
||||||
// _trafficManager->parseSchedules(schedulesToRead);
|
|
||||||
SG_LOG(SG_AI, SG_INFO, "parsing traffic schedules took:" << st.elapsedMSec() << "msec");
|
|
||||||
|
|
||||||
SGGuard<SGMutex> g(_lock);
|
SGGuard<SGMutex> g(_lock);
|
||||||
_isFinished = true;
|
_isFinished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void startXML()
|
||||||
|
{
|
||||||
|
//cout << "Start XML" << endl;
|
||||||
|
requiredAircraft = "";
|
||||||
|
homePort = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void endXML()
|
||||||
|
{
|
||||||
|
//cout << "End XML" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startElement(const char *name,
|
||||||
|
const XMLAttributes & atts)
|
||||||
|
{
|
||||||
|
const char *attval;
|
||||||
|
//cout << "Start element " << name << endl;
|
||||||
|
//FGTrafficManager temp;
|
||||||
|
//for (int i = 0; i < atts.size(); i++)
|
||||||
|
// if (string(atts.getName(i)) == string("include"))
|
||||||
|
attval = atts.getValue("include");
|
||||||
|
if (attval != 0) {
|
||||||
|
//cout << "including " << attval << endl;
|
||||||
|
SGPath path = globals->get_fg_root();
|
||||||
|
path.append("/Traffic/");
|
||||||
|
path.append(attval);
|
||||||
|
readXML(path.str(), *this);
|
||||||
|
}
|
||||||
|
elementValueStack.push_back("");
|
||||||
|
// cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void endElement(const char *name)
|
||||||
|
{
|
||||||
|
//cout << "End element " << name << endl;
|
||||||
|
const string & value = elementValueStack.back();
|
||||||
|
|
||||||
|
if (!strcmp(name, "model"))
|
||||||
|
mdl = value;
|
||||||
|
else if (!strcmp(name, "livery"))
|
||||||
|
livery = value;
|
||||||
|
else if (!strcmp(name, "home-port"))
|
||||||
|
homePort = value;
|
||||||
|
else if (!strcmp(name, "registration"))
|
||||||
|
registration = value;
|
||||||
|
else if (!strcmp(name, "airline"))
|
||||||
|
airline = value;
|
||||||
|
else if (!strcmp(name, "actype"))
|
||||||
|
acType = value;
|
||||||
|
else if (!strcmp(name, "required-aircraft"))
|
||||||
|
requiredAircraft = value;
|
||||||
|
else if (!strcmp(name, "flighttype"))
|
||||||
|
flighttype = value;
|
||||||
|
else if (!strcmp(name, "radius"))
|
||||||
|
radius = atoi(value.c_str());
|
||||||
|
else if (!strcmp(name, "offset"))
|
||||||
|
offset = atoi(value.c_str());
|
||||||
|
else if (!strcmp(name, "performance-class"))
|
||||||
|
m_class = value;
|
||||||
|
else if (!strcmp(name, "heavy")) {
|
||||||
|
if (value == string("true"))
|
||||||
|
heavy = true;
|
||||||
|
else
|
||||||
|
heavy = false;
|
||||||
|
} else if (!strcmp(name, "callsign"))
|
||||||
|
callsign = value;
|
||||||
|
else if (!strcmp(name, "fltrules"))
|
||||||
|
fltrules = value;
|
||||||
|
else if (!strcmp(name, "port"))
|
||||||
|
port = value;
|
||||||
|
else if (!strcmp(name, "time"))
|
||||||
|
timeString = value;
|
||||||
|
else if (!strcmp(name, "departure")) {
|
||||||
|
departurePort = port;
|
||||||
|
departureTime = timeString;
|
||||||
|
} else if (!strcmp(name, "cruise-alt"))
|
||||||
|
cruiseAlt = atoi(value.c_str());
|
||||||
|
else if (!strcmp(name, "arrival")) {
|
||||||
|
arrivalPort = port;
|
||||||
|
arrivalTime = timeString;
|
||||||
|
} else if (!strcmp(name, "repeat"))
|
||||||
|
repeat = value;
|
||||||
|
else if (!strcmp(name, "flight")) {
|
||||||
|
// We have loaded and parsed all the information belonging to this flight
|
||||||
|
// so we temporarily store it.
|
||||||
|
//cerr << "Pusing back flight " << callsign << endl;
|
||||||
|
//cerr << callsign << " " << fltrules << " "<< departurePort << " " << arrivalPort << " "
|
||||||
|
// << cruiseAlt << " " << departureTime<< " "<< arrivalTime << " " << repeat << endl;
|
||||||
|
|
||||||
|
//Prioritize aircraft
|
||||||
|
string apt = fgGetString("/sim/presets/airport-id");
|
||||||
|
//cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl;
|
||||||
|
//if (departurePort == apt) score++;
|
||||||
|
//flights.push_back(new FGScheduledFlight(callsign,
|
||||||
|
// fltrules,
|
||||||
|
// departurePort,
|
||||||
|
// arrivalPort,
|
||||||
|
// cruiseAlt,
|
||||||
|
// departureTime,
|
||||||
|
// arrivalTime,
|
||||||
|
// repeat));
|
||||||
|
if (requiredAircraft == "") {
|
||||||
|
char buffer[16];
|
||||||
|
snprintf(buffer, 16, "%d", acCounter);
|
||||||
|
requiredAircraft = buffer;
|
||||||
|
}
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Adding flight: " << callsign << " "
|
||||||
|
<< fltrules << " "
|
||||||
|
<< departurePort << " "
|
||||||
|
<< arrivalPort << " "
|
||||||
|
<< cruiseAlt << " "
|
||||||
|
<< departureTime << " "
|
||||||
|
<< arrivalTime << " " << repeat << " " << requiredAircraft);
|
||||||
|
// For database maintainance purposes, it may be convenient to
|
||||||
|
//
|
||||||
|
if (fgGetBool("/sim/traffic-manager/dumpdata") == true) {
|
||||||
|
SG_LOG(SG_AI, SG_ALERT, "Traffic Dump FLIGHT," << callsign << ","
|
||||||
|
<< fltrules << ","
|
||||||
|
<< departurePort << ","
|
||||||
|
<< arrivalPort << ","
|
||||||
|
<< cruiseAlt << ","
|
||||||
|
<< departureTime << ","
|
||||||
|
<< arrivalTime << "," << repeat << "," << requiredAircraft);
|
||||||
|
}
|
||||||
|
|
||||||
|
_trafficManager->flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
|
||||||
|
fltrules,
|
||||||
|
departurePort,
|
||||||
|
arrivalPort,
|
||||||
|
cruiseAlt,
|
||||||
|
departureTime,
|
||||||
|
arrivalTime,
|
||||||
|
repeat,
|
||||||
|
requiredAircraft));
|
||||||
|
requiredAircraft = "";
|
||||||
|
} else if (!strcmp(name, "aircraft")) {
|
||||||
|
endAircraft();
|
||||||
|
}
|
||||||
|
|
||||||
|
elementValueStack.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void data(const char *s, int len)
|
||||||
|
{
|
||||||
|
string token = string(s, len);
|
||||||
|
//cout << "Character data " << string(s,len) << endl;
|
||||||
|
elementValueStack.back() += token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pi(const char *target, const char *data)
|
||||||
|
{
|
||||||
|
//cout << "Processing instruction " << target << ' ' << data << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void warning(const char *message, int line, int column)
|
||||||
|
{
|
||||||
|
SG_LOG(SG_IO, SG_WARN,
|
||||||
|
"Warning: " << message << " (" << line << ',' << column << ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
void error(const char *message, int line, int column)
|
||||||
|
{
|
||||||
|
SG_LOG(SG_IO, SG_ALERT,
|
||||||
|
"Error: " << message << " (" << line << ',' << column << ')');
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void endAircraft()
|
||||||
|
{
|
||||||
|
string isHeavy = heavy ? "true" : "false";
|
||||||
|
|
||||||
|
if (missingModels.find(mdl) != missingModels.end()) {
|
||||||
|
// don't stat() or warn again
|
||||||
|
requiredAircraft = homePort = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FGAISchedule::validModelPath(mdl)) {
|
||||||
|
missingModels.insert(mdl);
|
||||||
|
SG_LOG(SG_AI, SG_WARN, "TrafficMgr: Missing model path:" << mdl);
|
||||||
|
requiredAircraft = homePort = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int proportion =
|
||||||
|
(int) (fgGetDouble("/sim/traffic-manager/proportion") * 100);
|
||||||
|
int randval = rand() & 100;
|
||||||
|
if (randval > proportion) {
|
||||||
|
requiredAircraft = homePort = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fgGetBool("/sim/traffic-manager/dumpdata") == true) {
|
||||||
|
SG_LOG(SG_AI, SG_ALERT, "Traffic Dump AC," << homePort << "," << registration << "," << requiredAircraft
|
||||||
|
<< "," << acType << "," << livery << ","
|
||||||
|
<< airline << "," << m_class << "," << offset << "," << radius << "," << flighttype << "," << isHeavy << "," << mdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requiredAircraft == "") {
|
||||||
|
char buffer[16];
|
||||||
|
snprintf(buffer, 16, "%d", acCounter);
|
||||||
|
requiredAircraft = buffer;
|
||||||
|
}
|
||||||
|
if (homePort == "") {
|
||||||
|
homePort = departurePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
// caution, modifying the scheduled aircraft strucutre from the
|
||||||
|
// 'wrong' thread. This is safe becuase FGTrafficManager won't touch
|
||||||
|
// the structure while we exist.
|
||||||
|
_trafficManager->scheduledAircraft.push_back(new FGAISchedule(mdl,
|
||||||
|
livery,
|
||||||
|
homePort,
|
||||||
|
registration,
|
||||||
|
requiredAircraft,
|
||||||
|
heavy,
|
||||||
|
acType,
|
||||||
|
airline,
|
||||||
|
m_class,
|
||||||
|
flighttype,
|
||||||
|
radius, offset));
|
||||||
|
|
||||||
|
acCounter++;
|
||||||
|
requiredAircraft = "";
|
||||||
|
homePort = "";
|
||||||
|
score = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseTrafficDir(const SGPath& path)
|
||||||
|
{
|
||||||
|
SGTimeStamp st;
|
||||||
|
st.stamp();
|
||||||
|
|
||||||
|
simgear::Dir trafficDir(path);
|
||||||
|
simgear::PathList d = trafficDir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
|
||||||
|
|
||||||
|
BOOST_FOREACH(SGPath p, d) {
|
||||||
|
simgear::Dir d2(p);
|
||||||
|
simgear::PathList trafficFiles = d2.children(simgear::Dir::TYPE_FILE, ".xml");
|
||||||
|
BOOST_FOREACH(SGPath xml, trafficFiles) {
|
||||||
|
readXML(xml.str(), *this);
|
||||||
|
if (_cancelThread) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // of sub-directories iteration
|
||||||
|
|
||||||
|
SG_LOG(SG_AI, SG_INFO, "parsing traffic schedules took:" << st.elapsedMSec() << "msec");
|
||||||
|
}
|
||||||
|
|
||||||
FGTrafficManager* _trafficManager;
|
FGTrafficManager* _trafficManager;
|
||||||
mutable SGMutex _lock;
|
mutable SGMutex _lock;
|
||||||
bool _isFinished;
|
bool _isFinished;
|
||||||
bool _cancelThread;
|
bool _cancelThread;
|
||||||
SGPath _trafficDirPath;
|
PathList _trafficDirPaths;
|
||||||
|
|
||||||
|
// parser state
|
||||||
|
|
||||||
|
string_list elementValueStack;
|
||||||
|
// record model paths which are missing, to avoid duplicate
|
||||||
|
// warnings when parsing traffic schedules.
|
||||||
|
std::set<std::string> missingModels;
|
||||||
|
|
||||||
|
std::string mdl, livery, registration, callsign, fltrules,
|
||||||
|
port, timeString, departurePort, departureTime, arrivalPort, arrivalTime,
|
||||||
|
repeat, acType, airline, m_class, flighttype, requiredAircraft, homePort;
|
||||||
|
int cruiseAlt;
|
||||||
|
int score, acCounter;
|
||||||
|
double radius, offset;
|
||||||
|
bool heavy;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
|
@ -151,12 +411,6 @@ FGTrafficManager::FGTrafficManager() :
|
||||||
inited(false),
|
inited(false),
|
||||||
doingInit(false),
|
doingInit(false),
|
||||||
waitingMetarTime(0.0),
|
waitingMetarTime(0.0),
|
||||||
cruiseAlt(0),
|
|
||||||
score(0),
|
|
||||||
acCounter(0),
|
|
||||||
radius(0),
|
|
||||||
offset(0),
|
|
||||||
heavy(false),
|
|
||||||
enabled("/sim/traffic-manager/enabled"),
|
enabled("/sim/traffic-manager/enabled"),
|
||||||
aiEnabled("/sim/ai/enabled"),
|
aiEnabled("/sim/ai/enabled"),
|
||||||
realWxEnabled("/environment/realwx/enabled"),
|
realWxEnabled("/environment/realwx/enabled"),
|
||||||
|
@ -224,14 +478,6 @@ void FGTrafficManager::shutdown()
|
||||||
inited = false;
|
inited = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// caution - this is run on the helper thread to improve startup
|
|
||||||
/// responsiveness - do not access properties or global state from
|
|
||||||
/// here, since there's no locking protection at all
|
|
||||||
void FGTrafficManager::parseSchedule(const SGPath& path)
|
|
||||||
{
|
|
||||||
readXML(path.str(), *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::init()
|
void FGTrafficManager::init()
|
||||||
{
|
{
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
|
@ -240,9 +486,15 @@ void FGTrafficManager::init()
|
||||||
|
|
||||||
assert(!doingInit);
|
assert(!doingInit);
|
||||||
doingInit = true;
|
doingInit = true;
|
||||||
if (string(fgGetString("/sim/traffic-manager/datafile")).empty()) {
|
if (string(fgGetString("/sim/traffic-manager/datafile")).empty()) {
|
||||||
|
PathList dirs = globals->get_data_paths("AI/Traffic");
|
||||||
|
if (dirs.empty()) {
|
||||||
|
doingInit = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scheduleParser.reset(new ScheduleParseThread(this));
|
scheduleParser.reset(new ScheduleParseThread(this));
|
||||||
scheduleParser->setTrafficDir(SGPath(globals->get_fg_root(), "AI/Traffic"));
|
scheduleParser->setTrafficDirs(dirs);
|
||||||
scheduleParser->start();
|
scheduleParser->start();
|
||||||
} else {
|
} else {
|
||||||
fgSetBool("/sim/traffic-manager/heuristics", false);
|
fgSetBool("/sim/traffic-manager/heuristics", false);
|
||||||
|
@ -250,7 +502,10 @@ void FGTrafficManager::init()
|
||||||
string ext = path.extension();
|
string ext = path.extension();
|
||||||
if (path.extension() == "xml") {
|
if (path.extension() == "xml") {
|
||||||
if (path.exists()) {
|
if (path.exists()) {
|
||||||
readXML(path.str(), *this);
|
// use a SchedulerParser to parse, but run it in this thread,
|
||||||
|
// i.e don't start it
|
||||||
|
ScheduleParseThread parser(this);
|
||||||
|
readXML(path.str(), parser);
|
||||||
}
|
}
|
||||||
} else if (path.extension() == "conf") {
|
} else if (path.extension() == "conf") {
|
||||||
if (path.exists()) {
|
if (path.exists()) {
|
||||||
|
@ -597,225 +852,3 @@ void FGTrafficManager::Tokenize(const string& str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void FGTrafficManager::startXML()
|
|
||||||
{
|
|
||||||
//cout << "Start XML" << endl;
|
|
||||||
requiredAircraft = "";
|
|
||||||
homePort = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::endXML()
|
|
||||||
{
|
|
||||||
//cout << "End XML" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::startElement(const char *name,
|
|
||||||
const XMLAttributes & atts)
|
|
||||||
{
|
|
||||||
const char *attval;
|
|
||||||
//cout << "Start element " << name << endl;
|
|
||||||
//FGTrafficManager temp;
|
|
||||||
//for (int i = 0; i < atts.size(); i++)
|
|
||||||
// if (string(atts.getName(i)) == string("include"))
|
|
||||||
attval = atts.getValue("include");
|
|
||||||
if (attval != 0) {
|
|
||||||
//cout << "including " << attval << endl;
|
|
||||||
SGPath path = globals->get_fg_root();
|
|
||||||
path.append("/Traffic/");
|
|
||||||
path.append(attval);
|
|
||||||
readXML(path.str(), *this);
|
|
||||||
}
|
|
||||||
elementValueStack.push_back("");
|
|
||||||
// cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::endElement(const char *name)
|
|
||||||
{
|
|
||||||
//cout << "End element " << name << endl;
|
|
||||||
const string & value = elementValueStack.back();
|
|
||||||
|
|
||||||
if (!strcmp(name, "model"))
|
|
||||||
mdl = value;
|
|
||||||
else if (!strcmp(name, "livery"))
|
|
||||||
livery = value;
|
|
||||||
else if (!strcmp(name, "home-port"))
|
|
||||||
homePort = value;
|
|
||||||
else if (!strcmp(name, "registration"))
|
|
||||||
registration = value;
|
|
||||||
else if (!strcmp(name, "airline"))
|
|
||||||
airline = value;
|
|
||||||
else if (!strcmp(name, "actype"))
|
|
||||||
acType = value;
|
|
||||||
else if (!strcmp(name, "required-aircraft"))
|
|
||||||
requiredAircraft = value;
|
|
||||||
else if (!strcmp(name, "flighttype"))
|
|
||||||
flighttype = value;
|
|
||||||
else if (!strcmp(name, "radius"))
|
|
||||||
radius = atoi(value.c_str());
|
|
||||||
else if (!strcmp(name, "offset"))
|
|
||||||
offset = atoi(value.c_str());
|
|
||||||
else if (!strcmp(name, "performance-class"))
|
|
||||||
m_class = value;
|
|
||||||
else if (!strcmp(name, "heavy")) {
|
|
||||||
if (value == string("true"))
|
|
||||||
heavy = true;
|
|
||||||
else
|
|
||||||
heavy = false;
|
|
||||||
} else if (!strcmp(name, "callsign"))
|
|
||||||
callsign = value;
|
|
||||||
else if (!strcmp(name, "fltrules"))
|
|
||||||
fltrules = value;
|
|
||||||
else if (!strcmp(name, "port"))
|
|
||||||
port = value;
|
|
||||||
else if (!strcmp(name, "time"))
|
|
||||||
timeString = value;
|
|
||||||
else if (!strcmp(name, "departure")) {
|
|
||||||
departurePort = port;
|
|
||||||
departureTime = timeString;
|
|
||||||
} else if (!strcmp(name, "cruise-alt"))
|
|
||||||
cruiseAlt = atoi(value.c_str());
|
|
||||||
else if (!strcmp(name, "arrival")) {
|
|
||||||
arrivalPort = port;
|
|
||||||
arrivalTime = timeString;
|
|
||||||
} else if (!strcmp(name, "repeat"))
|
|
||||||
repeat = value;
|
|
||||||
else if (!strcmp(name, "flight")) {
|
|
||||||
// We have loaded and parsed all the information belonging to this flight
|
|
||||||
// so we temporarily store it.
|
|
||||||
//cerr << "Pusing back flight " << callsign << endl;
|
|
||||||
//cerr << callsign << " " << fltrules << " "<< departurePort << " " << arrivalPort << " "
|
|
||||||
// << cruiseAlt << " " << departureTime<< " "<< arrivalTime << " " << repeat << endl;
|
|
||||||
|
|
||||||
//Prioritize aircraft
|
|
||||||
string apt = fgGetString("/sim/presets/airport-id");
|
|
||||||
//cerr << "Airport information: " << apt << " " << departurePort << " " << arrivalPort << endl;
|
|
||||||
//if (departurePort == apt) score++;
|
|
||||||
//flights.push_back(new FGScheduledFlight(callsign,
|
|
||||||
// fltrules,
|
|
||||||
// departurePort,
|
|
||||||
// arrivalPort,
|
|
||||||
// cruiseAlt,
|
|
||||||
// departureTime,
|
|
||||||
// arrivalTime,
|
|
||||||
// repeat));
|
|
||||||
if (requiredAircraft == "") {
|
|
||||||
char buffer[16];
|
|
||||||
snprintf(buffer, 16, "%d", acCounter);
|
|
||||||
requiredAircraft = buffer;
|
|
||||||
}
|
|
||||||
SG_LOG(SG_AI, SG_DEBUG, "Adding flight: " << callsign << " "
|
|
||||||
<< fltrules << " "
|
|
||||||
<< departurePort << " "
|
|
||||||
<< arrivalPort << " "
|
|
||||||
<< cruiseAlt << " "
|
|
||||||
<< departureTime << " "
|
|
||||||
<< arrivalTime << " " << repeat << " " << requiredAircraft);
|
|
||||||
// For database maintainance purposes, it may be convenient to
|
|
||||||
//
|
|
||||||
if (fgGetBool("/sim/traffic-manager/dumpdata") == true) {
|
|
||||||
SG_LOG(SG_AI, SG_ALERT, "Traffic Dump FLIGHT," << callsign << ","
|
|
||||||
<< fltrules << ","
|
|
||||||
<< departurePort << ","
|
|
||||||
<< arrivalPort << ","
|
|
||||||
<< cruiseAlt << ","
|
|
||||||
<< departureTime << ","
|
|
||||||
<< arrivalTime << "," << repeat << "," << requiredAircraft);
|
|
||||||
}
|
|
||||||
flights[requiredAircraft].push_back(new FGScheduledFlight(callsign,
|
|
||||||
fltrules,
|
|
||||||
departurePort,
|
|
||||||
arrivalPort,
|
|
||||||
cruiseAlt,
|
|
||||||
departureTime,
|
|
||||||
arrivalTime,
|
|
||||||
repeat,
|
|
||||||
requiredAircraft));
|
|
||||||
requiredAircraft = "";
|
|
||||||
} else if (!strcmp(name, "aircraft")) {
|
|
||||||
endAircraft();
|
|
||||||
}
|
|
||||||
|
|
||||||
elementValueStack.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::endAircraft()
|
|
||||||
{
|
|
||||||
string isHeavy = heavy ? "true" : "false";
|
|
||||||
|
|
||||||
if (missingModels.find(mdl) != missingModels.end()) {
|
|
||||||
// don't stat() or warn again
|
|
||||||
requiredAircraft = homePort = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FGAISchedule::validModelPath(mdl)) {
|
|
||||||
missingModels.insert(mdl);
|
|
||||||
SG_LOG(SG_AI, SG_WARN, "TrafficMgr: Missing model path:" << mdl);
|
|
||||||
requiredAircraft = homePort = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int proportion =
|
|
||||||
(int) (fgGetDouble("/sim/traffic-manager/proportion") * 100);
|
|
||||||
int randval = rand() & 100;
|
|
||||||
if (randval > proportion) {
|
|
||||||
requiredAircraft = homePort = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fgGetBool("/sim/traffic-manager/dumpdata") == true) {
|
|
||||||
SG_LOG(SG_AI, SG_ALERT, "Traffic Dump AC," << homePort << "," << registration << "," << requiredAircraft
|
|
||||||
<< "," << acType << "," << livery << ","
|
|
||||||
<< airline << "," << m_class << "," << offset << "," << radius << "," << flighttype << "," << isHeavy << "," << mdl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requiredAircraft == "") {
|
|
||||||
char buffer[16];
|
|
||||||
snprintf(buffer, 16, "%d", acCounter);
|
|
||||||
requiredAircraft = buffer;
|
|
||||||
}
|
|
||||||
if (homePort == "") {
|
|
||||||
homePort = departurePort;
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduledAircraft.push_back(new FGAISchedule(mdl,
|
|
||||||
livery,
|
|
||||||
homePort,
|
|
||||||
registration,
|
|
||||||
requiredAircraft,
|
|
||||||
heavy,
|
|
||||||
acType,
|
|
||||||
airline,
|
|
||||||
m_class,
|
|
||||||
flighttype,
|
|
||||||
radius, offset));
|
|
||||||
|
|
||||||
acCounter++;
|
|
||||||
requiredAircraft = "";
|
|
||||||
homePort = "";
|
|
||||||
score = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::data(const char *s, int len)
|
|
||||||
{
|
|
||||||
string token = string(s, len);
|
|
||||||
//cout << "Character data " << string(s,len) << endl;
|
|
||||||
elementValueStack.back() += token;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::pi(const char *target, const char *data)
|
|
||||||
{
|
|
||||||
//cout << "Processing instruction " << target << ' ' << data << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::warning(const char *message, int line, int column)
|
|
||||||
{
|
|
||||||
SG_LOG(SG_IO, SG_WARN,
|
|
||||||
"Warning: " << message << " (" << line << ',' << column << ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
void FGTrafficManager::error(const char *message, int line, int column)
|
|
||||||
{
|
|
||||||
SG_LOG(SG_IO, SG_ALERT,
|
|
||||||
"Error: " << message << " (" << line << ',' << column << ')');
|
|
||||||
}
|
|
||||||
|
|
|
@ -51,9 +51,7 @@
|
||||||
|
|
||||||
#include <simgear/structure/subsystem_mgr.hxx>
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
#include <simgear/props/propertyObject.hxx>
|
#include <simgear/props/propertyObject.hxx>
|
||||||
#include <simgear/xml/easyxml.hxx>
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
#include <simgear/misc/sg_dir.hxx>
|
|
||||||
|
|
||||||
#include "SchedFlight.hxx"
|
#include "SchedFlight.hxx"
|
||||||
#include "Schedule.hxx"
|
#include "Schedule.hxx"
|
||||||
|
@ -77,7 +75,7 @@ typedef HeuristicMap::iterator HeuristicMapIterator;
|
||||||
|
|
||||||
class ScheduleParseThread;
|
class ScheduleParseThread;
|
||||||
|
|
||||||
class FGTrafficManager : public SGSubsystem, public XMLVisitor
|
class FGTrafficManager : public SGSubsystem
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
bool inited;
|
bool inited;
|
||||||
|
@ -87,24 +85,11 @@ private:
|
||||||
|
|
||||||
ScheduleVector scheduledAircraft;
|
ScheduleVector scheduledAircraft;
|
||||||
ScheduleVectorIterator currAircraft, currAircraftClosest;
|
ScheduleVectorIterator currAircraft, currAircraftClosest;
|
||||||
vector<string> elementValueStack;
|
|
||||||
|
|
||||||
// record model paths which are missing, to avoid duplicate
|
|
||||||
// warnings when parsing traffic schedules.
|
|
||||||
std::set<std::string> missingModels;
|
|
||||||
|
|
||||||
std::string mdl, livery, registration, callsign, fltrules,
|
|
||||||
port, timeString, departurePort, departureTime, arrivalPort, arrivalTime,
|
|
||||||
repeat, acType, airline, m_class, flighttype, requiredAircraft, homePort;
|
|
||||||
int cruiseAlt;
|
|
||||||
int score, acCounter;
|
|
||||||
double radius, offset;
|
|
||||||
bool heavy;
|
|
||||||
|
|
||||||
FGScheduledFlightMap flights;
|
FGScheduledFlightMap flights;
|
||||||
|
|
||||||
void readTimeTableFromFile(SGPath infilename);
|
void readTimeTableFromFile(SGPath infilename);
|
||||||
void Tokenize(const string& str, vector<string>& tokens, const string& delimiters = " ");
|
void Tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters = " ");
|
||||||
|
|
||||||
simgear::PropertyObject<bool> enabled, aiEnabled, realWxEnabled, metarValid;
|
simgear::PropertyObject<bool> enabled, aiEnabled, realWxEnabled, metarValid;
|
||||||
|
|
||||||
|
@ -129,20 +114,9 @@ public:
|
||||||
void init();
|
void init();
|
||||||
void update(double time);
|
void update(double time);
|
||||||
|
|
||||||
FGScheduledFlightVecIterator getFirstFlight(const string &ref) { return flights[ref].begin(); }
|
FGScheduledFlightVecIterator getFirstFlight(const std::string &ref) { return flights[ref].begin(); }
|
||||||
FGScheduledFlightVecIterator getLastFlight(const string &ref) { return flights[ref].end(); }
|
FGScheduledFlightVecIterator getLastFlight(const std::string &ref) { return flights[ref].end(); }
|
||||||
|
|
||||||
void endAircraft();
|
|
||||||
|
|
||||||
// Some overloaded virtual XMLVisitor members
|
|
||||||
virtual void startXML ();
|
|
||||||
virtual void endXML ();
|
|
||||||
virtual void startElement (const char * name, const XMLAttributes &atts);
|
|
||||||
virtual void endElement (const char * name);
|
|
||||||
virtual void data (const char * s, int len);
|
|
||||||
virtual void pi (const char * target, const char * data);
|
|
||||||
virtual void warning (const char * message, int line, int column);
|
|
||||||
virtual void error (const char * message, int line, int column);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue