};
-FGAIAircraft::FGAIAircraft(FGAIManager* mgr) {
+FGAIAircraft::FGAIAircraft(FGAIManager* mgr, FGAISchedule *ref) {
+ trafficRef = ref;
manager = mgr;
_type_str = "aircraft";
_otype = otAircraft;
FGAIAircraft::dt = dt;
- if (fp) ProcessFlightPlan(dt);
+ if (fp)
+ {
+ ProcessFlightPlan(dt);
+ time_t now = time(NULL) + fgGetLong("/sim/time/warp");
+ if (now < fp->getStartTime())
+ return;
+ //ProcessFlightPlan(dt);
+ }
double turn_radius_ft;
double turn_circum_ft;
}
void FGAIAircraft::ProcessFlightPlan( double dt ) {
+
+
FGAIFlightPlan::waypoint* prev = 0; // the one behind you
FGAIFlightPlan::waypoint* curr = 0; // the one ahead
FGAIFlightPlan::waypoint* next = 0; // the next plus 1
if ( dist_to_go < lead_dist ) {
if (curr->finished) { //end of the flight plan, so terminate
- setDie(true);
- return;
+ if (trafficRef)
+ {
+ delete fp;
+ //time_t now = time(NULL) + fgGetLong("/sim/time/warp");
+ trafficRef->next();
+
+ FGAIModelEntity entity;
+ entity.m_class = "jet_transport";
+ //entity.path = modelPath.c_str();
+ entity.flightplan = "none";
+ entity.latitude = _getLatitude();
+ entity.longitude = _getLongitude();
+ entity.altitude = trafficRef->getCruiseAlt() * 100; // convert from FL to feet
+ entity.speed = 450;
+ //entity.fp = new FGAIFlightPlan(&entity, courseToDest, i->getDepartureTime(), dep, arr);
+ entity.fp = new FGAIFlightPlan(&entity,
+ 999, // A hack
+ trafficRef->getDepartureTime(),
+ trafficRef->getDepartureAirport(),
+ trafficRef->getArrivalAirport());
+ SetFlightPlan(entity.fp);
+ }
+ else
+ {
+ setDie(true);
+ return;
+ }
}
- // we've reached the lead-point for the waypoint ahead
+ // we've reached the lead-point for the waypoint ahead
if (next) tgt_heading = fp->getBearing(curr, next);
fp->IncrementWaypoint();
prev = fp->getPreviousWaypoint();
}
+
bool FGAIAircraft::_getGearDown() const {
return ((props->getFloatValue("position/altitude-agl-ft") < 900.0)
&& (props->getFloatValue("velocities/airspeed-kt")
#include "AIManager.hxx"
#include "AIBase.hxx"
+#include <Traffic/SchedFlight.hxx>
+#include <Traffic/Schedule.hxx>
+
#include <string>
SG_USING_STD(string);
enum aircraft_e {LIGHT=0, WW2_FIGHTER, JET_TRANSPORT, JET_FIGHTER, TANKER};
static const PERF_STRUCT settings[];
- FGAIAircraft(FGAIManager* mgr);
+ FGAIAircraft(FGAIManager* mgr, FGAISchedule *ref=0);
~FGAIAircraft();
bool init();
inline void SetTanker(bool setting) { isTanker = setting; };
private:
-
+ FGAISchedule *trafficRef;
+
bool hdg_lock;
bool alt_lock;
double dt_count;
props = root->getNode(_type_str.c_str(), index, true);
if (model_path != "") {
- model = sgLoad3DModel( globals->get_fg_root(),
+ model = load3DModel( globals->get_fg_root(),
model_path.c_str(),
props,
globals->get_sim_time_sec() );
return true;
}
+
+ssgBranch * FGAIBase::load3DModel(const string& fg_root,
+ const string &path,
+ SGPropertyNode *prop_root,
+ double sim_time_sec)
+{
+ // some more code here to check whether a model with this name has already been loaded
+ // if not load it, otherwise, get the memory pointer and do something like
+ // SetModel as in ATC/AIEntity.cxx
+ //SSGBranch *model;
+ model = manager->getModel(path);
+ if (!(model))
+ {
+ model = sgLoad3DModel(fg_root,
+ path,
+ prop_root,
+ sim_time_sec);
+ manager->setModel(path, model);
+ model->ref();
+ }
+ //else
+ // {
+ // model->ref();
+ // aip.init(model);
+ // aip.setVisible(false);
+ // globals->get_scenery()->get_scene_graph()->addKid(aip.getSceneGraph());
+ // do some setModel stuff.
+ return model;
+}
+
bool FGAIBase::isa( object_type otype ) {
if ( otype == _otype ) { return true; }
else { return false; }
int _getID() const;
inline double _getRange() { return range; };
+ ssgBranch * load3DModel(const string& fg_root,
+ const string &path,
+ SGPropertyNode *prop_root,
+ double sim_time_sec);
static bool _isNight();
};
FGAIFlightPlan::FGAIFlightPlan(string filename)
{
int i;
+ start_time = 0;
SGPath path( globals->get_fg_root() );
path.append( ("/Data/AI/FlightPlans/" + filename).c_str() );
SGPropertyNode root;
// as setting speeds and altitude computed by the
// traffic manager.
FGAIFlightPlan::FGAIFlightPlan(FGAIModelEntity *entity,
- double course,
+ double course,
+ time_t start,
FGAirport *dep,
FGAirport *arr)
{
+ start_time = start;
bool useInitialWayPoint = true;
bool useCurrentWayPoint = false;
SGPath path( globals->get_fg_root() );
path.append( "/Data/AI/FlightPlans" );
path.append( entity->path );
SGPropertyNode root;
+
+ // This is a bit of a hack:
+ // Normally the value of course will be used to evaluate whether
+ // or not a waypoint will be used for midair initialization of
+ // an AI aircraft. However, if a course value of 999 will be passed
+ // when an update request is received, which will by definition always be
+ // on the ground and should include all waypoints.
+ if (course == 999)
+ {
+ useInitialWayPoint = false;
+ useCurrentWayPoint = true;
+ }
try {
readProperties(path.str(), &root);
(*i)->altitude);
double crse, crsDiff;
double dist;
- first.CourseAndDistance(curr, &crse, &dist);
+ curr.CourseAndDistance(first, &crse, &dist);
dist *= SG_METER_TO_NM;
// so once is the useWpt flag is set to true, we cannot reset it to false.
//cerr << "Discarding waypoint: " << (*i)->name
// << ": Course difference = " << crsDiff
- // << "Course = " << course
- // << "crse = " << crse << endl;
+ // << "Course = " << course
+ // << "crse = " << crse << endl;
}
else
useCurrentWayPoint = true;
{
if ((dist > 100.0) && (useInitialWayPoint))
{
- //waypoints.push_back(init_waypoint);
+ //waypoints.push_back(init_waypoint);;
waypoints.insert(i, init_waypoint);
//cerr << "Using waypoint : " << init_waypoint->name << endl;
}
+ //if (useInitialWayPoint)
+ // {
+ // (*i)->speed = dist; // A hack
+ // }
//waypoints.push_back( wpt );
//cerr << "Using waypoint : " << (*i)->name
// << ": course diff : " << crsDiff
*/
void FGAIFlightPlan::create(FGAirport *dep, FGAirport *arr, double alt, double speed)
{
- double wind_speed;
+double wind_speed;
double wind_heading;
FGRunway rwy;
+ double lat, lon, az;
+ double lat2, lon2, az2;
+ int direction;
//waypoints.push_back(wpt);
// Create the outbound taxi leg, for now simplified as a
// Direct route from the airport center point to the start
// of the runway.
///////////////////////////////////////////////////////////
- //cerr << "Cruise Alt << " << alt << endl;
+ //cerr << "Cruise Alt << " << alt << endl;
+ // Temporary code to add some small random variation to aircraft parking positions;
+ direction = (rand() % 360);
+geo_direct_wgs_84 ( 0, dep->latitude, dep->longitude, direction,
+ 100,
+ &lat2, &lon2, &az2 );
waypoint *wpt = new waypoint;
wpt->name = dep->id; //wpt_node->getStringValue("name", "END");
- wpt->latitude = dep->latitude;
- wpt->longitude = dep->longitude;
+ wpt->latitude = lat2;
+ wpt->longitude = lon2;
wpt->altitude = dep->elevation + 19; // probably need to add some model height to it
wpt->speed = 15;
wpt->crossat = -10000;
wpt->gear_down = true;
wpt->flaps_down= true;
wpt->finished = false;
+ wpt->on_ground = true;
waypoints.push_back(wpt);
// Get the current active runway, based on code from David Luff
exit(1);
}
- double lat, lon, az;
- double lat2, lon2, az2;
+
double heading = rwy.heading;
double azimuth = heading + 180.0;
while ( azimuth >= 360.0 ) { azimuth -= 360.0; }
waypoints.push_back(wpt);
//Runway Threshold
geo_direct_wgs_84 ( 0, rwy.lat, rwy.lon, azimuth,
- rwy.length*0.45,
+ rwy.length*0.45 * SG_FEET_TO_METER,
&lat2, &lon2, &az2 );
wpt = new waypoint;
wpt->name = "Threshold"; //wpt_node->getStringValue("name", "END");
wpt->gear_down = true;
wpt->flaps_down= true;
wpt->finished = false;
- wpt->on_ground = false;
+ wpt->on_ground = true;
waypoints.push_back(wpt);
+direction = (rand() % 360);
+geo_direct_wgs_84 ( 0, arr->latitude, arr->longitude, direction,
+ 100,
+ &lat2, &lon2, &az2 );
+
// Add the final destination waypoint
wpt = new waypoint;
wpt->name = arr->id; //wpt_node->getStringValue("name", "END");
- wpt->latitude = arr->latitude;
- wpt->longitude = arr->longitude;
+ wpt->latitude = lat2;
+ wpt->longitude = lon2;
wpt->altitude = arr->elevation+19;
wpt->speed = 15;
wpt->crossat = -10000;
wpt->gear_down = true;
wpt->flaps_down= true;
wpt->finished = false;
- wpt->on_ground = false;
+ wpt->on_ground = true;
waypoints.push_back(wpt);
// And finally one more named "END"
wpt = new waypoint;
wpt->name = "END"; //wpt_node->getStringValue("name", "END");
- wpt->latitude = arr->latitude;
- wpt->longitude = arr->longitude;
+ wpt->latitude = lat2;
+ wpt->longitude = lon2;
wpt->altitude = 19;
wpt->speed = 15;
wpt->crossat = -10000;
// And finally one more named "EOF"
wpt = new waypoint;
wpt->name = "EOF"; //wpt_node->getStringValue("name", "END");
- wpt->latitude = arr->latitude;
- wpt->longitude = arr->longitude;
+ wpt->latitude = lat2;
+ wpt->longitude = lon2;
wpt->altitude = 19;
wpt->speed = 15;
wpt->crossat = -10000;
wpt->gear_down = true;
wpt->flaps_down= true;
wpt->finished = true;
- wpt->finished = true;
+ wpt->on_ground = true;
waypoints.push_back(wpt);
}
FGAIFlightPlan(string filename);
FGAIFlightPlan(FGAIModelEntity *entity,
double course,
+ time_t start,
FGAirport *dep,
FGAirport *arr);
~FGAIFlightPlan();
double getLeadDistance( void ) const {return lead_distance;}
double getBearing(waypoint* previous, waypoint* next);
double getBearing(double lat, double lon, waypoint* next);
+ time_t getStartTime() { return start_time; };
void create(FGAirport *dep, FGAirport *arr, double alt, double speed);
double distance_to_go;
double lead_distance;
+ time_t start_time;
};
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
+#include <Airports/simple.hxx>
+#include <Traffic/SchedFlight.hxx>
+#include <Traffic/Schedule.hxx>
+#include <Traffic/TrafficMgr.hxx>
+
#include <list>
#include "AIManager.hxx"
++ai_list_itr;
}
ai_list.clear();
+ ModelVecIterator i = loadedModels.begin();
+ while (i != loadedModels.end())
+ {
+ i->getModelId()->deRef();
+ }
}
// initialize these for finding nearest thermals
range_nearest = 10000.0;
strength = 0.0;
+ FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager");
if (!enabled)
return;
ai_list_itr = ai_list.begin();
while(ai_list_itr != ai_list.end()) {
if ((*ai_list_itr)->getDie()) {
+ tmgr->release((*ai_list_itr)->getID());
--numObjects[(*ai_list_itr)->getType()];
--numObjects[0];
(*ai_list_itr)->unbind();
void*
-FGAIManager::createAircraft( FGAIModelEntity *entity ) {
+FGAIManager::createAircraft( FGAIModelEntity *entity, FGAISchedule *ref) {
- FGAIAircraft* ai_plane = new FGAIAircraft(this);
+ FGAIAircraft* ai_plane = new FGAIAircraft(this, ref);
ai_list.push_back(ai_plane);
++numObjects[0];
++numObjects[FGAIBase::otAircraft];
delete s;
}
+// This code keeps track of models that have already been loaded
+// Eventually we'd prbably need to find a way to keep track of models
+// that are unloaded again
+ssgBranch * FGAIManager::getModel(const string& path)
+{
+ ModelVecIterator i = loadedModels.begin();
+ while (i != loadedModels.end())
+ {
+ if (i->getPath() == path)
+ return i->getModelId();
+ i++;
+ }
+ return 0;
+}
+
+void FGAIManager::setModel(const string& path, ssgBranch *model)
+{
+ loadedModels.push_back(FGModelID(path,model));
+}
+
+
//end AIManager.cxx
#include <AIModel/AIScenario.hxx>
#include <AIModel/AIFlightPlan.hxx>
+#include <Traffic/SchedFlight.hxx>
+#include <Traffic/Schedule.hxx>
+
SG_USING_STD(list);
+SG_USING_STD(vector);
+
+class FGModelID
+{
+private:
+ ssgBranch * model;
+ string path;
+public:
+ FGModelID(const string& pth, ssgBranch * mdl) { path =pth; model=mdl;};
+ ssgBranch *getModelId() { return model;};
+ const string & getPath() { return path;};
+};
+
+typedef vector<FGModelID> ModelVec;
+typedef vector<FGModelID>::iterator ModelVecIterator;
+
class FGAIThermal;
// on the heap and ***DELETED WHEN REMOVED!!!!!***
ai_list_type ai_list;
ai_list_iterator ai_list_itr;
+ ModelVec loadedModels;
public:
void update(double dt);
void* createBallistic( FGAIModelEntity *entity );
- void* createAircraft( FGAIModelEntity *entity );
+ void* createAircraft( FGAIModelEntity *entity, FGAISchedule *ref=0 );
void* createThermal( FGAIModelEntity *entity );
void* createStorm( FGAIModelEntity *entity );
void* createShip( FGAIModelEntity *entity );
void processScenario( string &filename );
+ ssgBranch * getModel(const string& path);
+ void setModel(const string& path, ssgBranch *model);
+
private:
bool initDone;
}
revrwyno = GetReverseRunwayNo(runwayno);
}
-
runway_map_iterator pos;
for ( pos = runways.lower_bound( aptid );
pos != runways.upper_bound( aptid ); ++pos)
// of the first listed flight.
sort(flights.begin(), flights.end());
FGScheduledFlightVecIterator i = flights.begin();
+ if (AIManagerRef)
+ {
+ // Check if this aircraft has been released.
+ FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
+ if (tmgr->isReleased(AIManagerRef))
+ AIManagerRef = 0;
+ }
+
if (!AIManagerRef)
{
userLatitude = fgGetDouble("/position/latitude-deg");
// This flight is in progress, so we need to calculate it's
// approximate position and -if in range- create an AIAircraft
// object for it.
- if ((i->getDepartureTime() < now) && (i->getArrivalTime() > now))
+ //if ((i->getDepartureTime() < now) && (i->getArrivalTime() > now))
+
+
+ // Part of this flight is in the future.
+ if (i->getArrivalTime() > now)
{
dep = i->getDepartureAirport();
arr = i->getArrivalAirport ();
// arrival airport, in degrees. From here we can interpolate the
// position of the aircraft by calculating the ratio between
// total time enroute and elapsed time enroute.
+
totalTimeEnroute = i->getArrivalTime() - i->getDepartureTime();
elapsedTimeEnroute = now - i->getDepartureTime();
remainingTimeEnroute = i->getArrivalTime() - now;
temp = sgCartToPolar3d(Point3D(newPos[0], newPos[1],newPos[2]));
- lat = temp.lat() * SG_RADIANS_TO_DEGREES;
- lon = temp.lon() * SG_RADIANS_TO_DEGREES;
+ if (now > i->getDepartureTime())
+ {
+ //cerr << "Lat = " << lat << ", lon = " << lon << endl;
+ //cerr << "Time diff: " << now-i->getDepartureTime() << endl;
+ lat = temp.lat() * SG_RADIANS_TO_DEGREES;
+ lon = temp.lon() * SG_RADIANS_TO_DEGREES;
+ //err << "Lat = " << lat << ", lon = " << lon << endl;
+ //cerr << "Time diff: " << now-i->getDepartureTime() << endl;
+ }
+ else
+ {
+ lat = dep->latitude;
+ lon = dep->longitude;
+ }
SGWayPoint current (lon,
lat,
i->getCruiseAlt());
// We really only need distance to user
// and course to destination
- current.CourseAndDistance(user, &courseToUser, &distanceToUser);
- current.CourseAndDistance(dest, &courseToDest, &distanceToDest);
+ user.CourseAndDistance(current, &courseToUser, &distanceToUser);
+ dest.CourseAndDistance(current, &courseToDest, &distanceToDest);
speed = (distanceToDest*SG_METER_TO_NM) /
((double) remainingTimeEnroute/3600.0);
{
string flightPlanName = dep->id + string("-") + arr->id +
string(".xml");
+ int alt;
+ //if ((i->getDepartureTime() < now))
+ //{
+ // alt = i->getCruiseAlt() *100;
+ // }
+ //else
+ //{
+ // alt = dep->elevation+19;
+ // }
FGAIModelEntity entity;
entity.flightplan = flightPlanName.c_str();
entity.latitude = lat;
entity.longitude = lon;
- entity.altitude = i->getCruiseAlt() * 100; // convert from FL to feet
+ entity.altitude = i->getCruiseAlt() *100; // convert from FL to feet
entity.speed = 450;
- entity.fp = new FGAIFlightPlan(&entity, courseToDest, dep, arr);
+ entity.fp = new FGAIFlightPlan(&entity, courseToDest, i->getDepartureTime(), dep, arr);
// Fixme: A non-existent model path results in an
// abort, due to an unhandled exeption, in fg main loop.
- AIManagerRef = aimgr->createAircraft( &entity );
+ AIManagerRef = aimgr->createAircraft( &entity, this);
//cerr << "Created: " << AIManagerRef << endl;
}
return;
// Both departure and arrival time are in the future, so this
// the aircraft is parked at the departure airport.
- // Currently this status is mostly ignored, but in furture
+ // Currently this status is mostly ignored, but in future
// versions, code should go here that -if within user range-
// positions these aircraft at parking locations at the airport.
if ((i->getDepartureTime() > now) && (i->getArrivalTime() > now))
}
}
}
+
+
+void FGAISchedule::next()
+{
+ flights.begin()->update();
+ sort(flights.begin(), flights.end());
+}
+
#define _FGSCHEDULE_HXX_
+
class FGAISchedule
{
private:
void* AIManagerRef;
bool firstRun;
+
public:
FGAISchedule(); // constructor
FGAISchedule(string, string, string, bool, FGScheduledFlightVec); // construct & init
~FGAISchedule(); //destructor
void update(time_t now);
+ void next(); // forces the schedule to move on to the next flight.
+
+ time_t getDepartureTime () { return flights.begin()->getDepartureTime (); };
+ FGAirport * getDepartureAirport () { return flights.begin()->getDepartureAirport(); };
+ FGAirport * getArrivalAirport () { return flights.begin()->getArrivalAirport (); };
+ int getCruiseAlt () { return flights.begin()->getCruiseAlt (); };
// More member functions follow later
};
currAircraft++;
}
+void FGTrafficManager::release(void *id)
+{
+ releaseList.push_back(id);
+}
+
+bool FGTrafficManager::isReleased(void *id)
+{
+ IdListIterator i = releaseList.begin();
+ while (i != releaseList.end())
+ {
+ if ((*i) == id)
+ {
+ releaseList.erase(i);
+ return true;
+ }
+ i++;
+ }
+ return false;
+}
+
void FGTrafficManager::startXML () {
//cout << "Start XML" << endl;
}
#include "Schedule.hxx"
+typedef vector<void *> IdList;
+typedef vector<void *>::iterator IdListIterator;
+
+
class FGTrafficManager : public SGSubsystem, public XMLVisitor
{
private:
repeat;
int cruiseAlt;
bool heavy;
+
+ IdList releaseList;
FGScheduledFlightVec flights;
void init();
void update(double time);
+ void release(void *ref);
+ bool isReleased(void *id);
// Some overloaded virtual XMLVisitor members
virtual void startXML ();