#include <simgear/sg_inlines.h>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/props/props.hxx>
-#include <simgear/route/waypoint.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/xml/easyxml.hxx>
#include <AIModel/AIFlightPlan.hxx>
#include <AIModel/AIManager.hxx>
#include <AIModel/AIAircraft.hxx>
-#include <Airports/simple.hxx>
+#include <Airports/airport.hxx>
#include <Main/fg_init.hxx> // That's pretty ugly, but I need fgFindAirportID
/******************************************************************************
* the FGAISchedule class contains data members and code to maintain a
- * schedule of Flights for an articically controlled aircraft.
+ * schedule of Flights for an artificially controlled aircraft.
*****************************************************************************/
FGAISchedule::FGAISchedule()
+ : heavy(false),
+ radius(0),
+ groundOffset(0),
+ distanceToUser(0),
+ score(0),
+ runCount(0),
+ hits(0),
+ lastRun(0),
+ firstRun(false),
+ courseToDest(0),
+ initialized(false),
+ valid(false),
+ scheduleComplete(false)
{
- firstRun = true;
- AIManagerRef = 0;
-
- heavy = false;
- radius = 0;
- groundOffset = 0;
- distanceToUser = 0;
- //score = 0;
}
-/*
-FGAISchedule::FGAISchedule(string mdl,
- string liv,
- string reg,
- bool hvy,
- string act,
- string arln,
- string mclass,
- string fltpe,
- double rad,
- double grnd,
- int scre,
- FGScheduledFlightVec flt)*/
-FGAISchedule::FGAISchedule(string model,
- string lvry,
- string port,
- string reg,
- string flightId,
+
+FGAISchedule::FGAISchedule(const string& model,
+ const string& lvry,
+ const string& port,
+ const string& reg,
+ const string& flightId,
bool hvy,
- string act,
- string arln,
- string mclass,
- string fltpe,
+ const string& act,
+ const string& arln,
+ const string& mclass,
+ const string& fltpe,
double rad,
double grnd)
+ : heavy(hvy),
+ radius(rad),
+ groundOffset(grnd),
+ distanceToUser(0),
+ score(0),
+ runCount(0),
+ hits(0),
+ lastRun(0),
+ firstRun(true),
+ courseToDest(0),
+ initialized(false),
+ valid(true),
+ scheduleComplete(false)
{
modelPath = model;
livery = lvry;
airline = arln;
m_class = mclass;
flightType = fltpe;
- radius = rad;
- groundOffset = grnd;
- distanceToUser = 0;
- heavy = hvy;
/*for (FGScheduledFlightVecIterator i = flt.begin();
i != flt.end();
i++)
flights.push_back(new FGScheduledFlight((*(*i))));*/
- AIManagerRef = 0;
- //score = scre;
- firstRun = true;
}
FGAISchedule::FGAISchedule(const FGAISchedule &other)
heavy = other.heavy;
flightIdentifier = other.flightIdentifier;
flights = other.flights;
- AIManagerRef = other.AIManagerRef;
+ aiAircraft = other.aiAircraft;
acType = other.acType;
airline = other.airline;
m_class = other.m_class;
radius = other.radius;
groundOffset = other.groundOffset;
flightType = other.flightType;
- //score = other.score;
+ score = other.score;
distanceToUser = other.distanceToUser;
currentDestination = other.currentDestination;
firstRun = other.firstRun;
+ runCount = other.runCount;
+ hits = other.hits;
+ lastRun = other.lastRun;
+ courseToDest = other.courseToDest;
+ initialized = other.initialized;
+ valid = other.valid;
+ scheduleComplete = other.scheduleComplete;
}
+
FGAISchedule::~FGAISchedule()
{
+ // remove related object from AI manager
+ if (aiAircraft)
+ {
+ aiAircraft->setDie(true);
+ }
+
/* for (FGScheduledFlightVecIterator flt = flights.begin(); flt != flights.end(); flt++)
{
delete (*flt);
return true;
}
+/**
+ * Returns true when processing is complete.
+ * Returns false when processing was aborted due to timeout, so
+ * more time required - and another call is requested (next sim iteration).
+ */
bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
-{
- if (!fgGetBool("/sim/traffic-manager/enabled"))
- return true;
-
- time_t
- totalTimeEnroute,
- elapsedTimeEnroute,
- remainingTimeEnroute,
- deptime = 0;
-
- scheduleFlights();
+{
+
+ time_t totalTimeEnroute,
+ elapsedTimeEnroute,
+ //remainingTimeEnroute,
+ deptime = 0;
+
+ if (!valid) {
+ return true; // processing complete
+ }
+
+ if (!scheduleComplete) {
+ scheduleComplete = scheduleFlights(now);
+ }
+
+ if (!scheduleComplete) {
+ return false; // not ready yet, continue processing in next iteration
+ }
+
if (flights.empty()) { // No flights available for this aircraft
- return false;
+ valid = false;
+ return true; // processing complete
}
-
+
+
// Sort all the scheduled flights according to scheduled departure time.
// Because this is done at every update, we only need to check the status
// of the first listed flight.
FGScheduledFlight* flight = flights.front();
if (!deptime) {
deptime = flight->getDepartureTime();
- //cerr << "Settiing departure time " << deptime << endl;
+ //cerr << "Setting departure time " << deptime << endl;
}
- if (AIManagerRef) {
- // Check if this aircraft has been released.
- FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
- if (tmgr->isReleased(AIManagerRef)) {
- AIManagerRef = NULL;
+ if (aiAircraft) {
+ if (aiAircraft->getDie()) {
+ aiAircraft = NULL;
} else {
return true; // in visual range, let the AIManager handle it
}
// This flight entry is entirely in the past, do we need to
// push it forward in time to the next scheduled departure.
if (flight->getArrivalTime() < now) {
- SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is in the Past");
+ SG_LOG (SG_AI, SG_BULK, "Traffic Manager: Flight is in the Past");
// Don't just update: check whether we need to load a new leg. etc.
// This update occurs for distant aircraft, so we can update the current leg
// and detach it from the current list of aircraft.
- flight->update();
+ flight->update();
flights.erase(flights.begin()); // pop_front(), effectively
- return true;
- }
+ return true; // processing complete
+ }
FGAirport* dep = flight->getDepartureAirport();
FGAirport* arr = flight->getArrivalAirport();
if (!dep || !arr) {
- return false;
+ return true; // processing complete
}
double speed = 450.0;
totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime();
if (flight->getDepartureTime() < now) {
elapsedTimeEnroute = now - flight->getDepartureTime();
- remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute;
+ //remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute;
double x = elapsedTimeEnroute / (double) totalTimeEnroute;
// current pos is based on great-circle course between departure/arrival,
SGGeodesy::direct(dep->geod(), course, coveredDistance, position, az2);
- SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is in progress, %=" << x);
+ SG_LOG (SG_AI, SG_BULK, "Traffic Manager: Flight is in progress, %=" << x);
speed = ((distanceM - coveredDistance) * SG_METER_TO_NM) / 3600.0;
} else {
// not departed yet
- remainingTimeEnroute = totalTimeEnroute;
+ //remainingTimeEnroute = totalTimeEnroute;
elapsedTimeEnroute = 0;
position = dep->geod();
- SG_LOG (SG_GENERAL, SG_BULK, "Traffic Manager: Flight is pending, departure in "
+ SG_LOG (SG_AI, SG_BULK, "Traffic Manager: Flight is pending, departure in "
<< flight->getDepartureTime() - now << " seconds ");
}
} else {
// departure / arrival coincident
- remainingTimeEnroute = totalTimeEnroute = 0.0;
+ //remainingTimeEnroute = totalTimeEnroute = 0.0;
elapsedTimeEnroute = 0;
position = dep->geod();
}
// large distances involved here: see bug #80
distanceToUser = dist(userCart, SGVec3d::fromGeod(position)) * SG_METER_TO_NM;
- // If distance between user and simulated aircaft is less
+
+ // If distance between user and simulated aircraft is less
// then 500nm, create this flight. At jet speeds 500 nm is roughly
// one hour flight time, so that would be a good approximate point
// to start a more detailed simulation of this aircraft.
- SG_LOG (SG_GENERAL, SG_BULK, "Traffic manager: " << registration << " is scheduled for a flight from "
+ SG_LOG (SG_AI, SG_BULK, "Traffic manager: " << registration << " is scheduled for a flight from "
<< dep->getId() << " to " << arr->getId() << ". Current distance to user: "
<< distanceToUser);
if (distanceToUser >= TRAFFICTOAIDISTTOSTART) {
return true; // out of visual range, for the moment.
}
-
- return createAIAircraft(flight, speed, deptime);
+
+ if (!createAIAircraft(flight, speed, deptime)) {
+ valid = false;
+ }
+
+
+ return true; // processing complete
+}
+
+bool FGAISchedule::validModelPath(const std::string& modelPath)
+{
+ SGPath mp(globals->get_fg_root());
+ SGPath mp_ai = mp;
+
+ mp.append(modelPath);
+ mp_ai.append("AI");
+ mp_ai.append(modelPath);
+
+ return mp.exists() || mp_ai.exists();
}
bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime)
FGAirport* dep = flight->getDepartureAirport();
FGAirport* arr = flight->getArrivalAirport();
string flightPlanName = dep->getId() + "-" + arr->getId() + ".xml";
- SG_LOG(SG_GENERAL, SG_INFO, "Traffic manager: Creating AIModel from:" << flightPlanName);
+ SG_LOG(SG_AI, SG_INFO, "Traffic manager: Creating AIModel from:" << flightPlanName);
// Only allow traffic to be created when the model path (or the AI version of mp) exists
SGPath mp(globals->get_fg_root());
mp_ai.append(modelPath);
if (!mp.exists() && !mp_ai.exists()) {
- SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.str());
+ SG_LOG(SG_AI, SG_WARN, "TrafficManager: Could not load model " << mp_ai.str());
return true;
}
- FGAIAircraft *aircraft = new FGAIAircraft(this);
- aircraft->setPerformance(m_class); //"jet_transport";
- aircraft->setCompany(airline); //i->getAirline();
- aircraft->setAcType(acType); //i->getAcType();
- aircraft->setPath(modelPath.c_str());
+ aiAircraft = new FGAIAircraft(this);
+ aiAircraft->setPerformance(acType, m_class); //"jet_transport";
+ aiAircraft->setCompany(airline); //i->getAirline();
+ aiAircraft->setAcType(acType); //i->getAcType();
+ aiAircraft->setPath(modelPath.c_str());
//aircraft->setFlightPlan(flightPlanName);
- aircraft->setLatitude(position.getLatitudeDeg());
- aircraft->setLongitude(position.getLongitudeDeg());
- aircraft->setAltitude(flight->getCruiseAlt()*100); // convert from FL to feet
- aircraft->setSpeed(speedKnots);
- aircraft->setBank(0);
+ aiAircraft->setLatitude(position.getLatitudeDeg());
+ aiAircraft->setLongitude(position.getLongitudeDeg());
+ aiAircraft->setAltitude(flight->getCruiseAlt()*100); // convert from FL to feet
+ aiAircraft->setSpeed(0);
+ aiAircraft->setBank(0);
courseToDest = SGGeodesy::courseDeg(position, arr->geod());
- aircraft->SetFlightPlan(new FGAIFlightPlan(aircraft, flightPlanName, courseToDest, deptime,
- dep, arr, true, radius,
- flight->getCruiseAlt()*100,
- position.getLatitudeDeg(),
- position.getLongitudeDeg(),
- speedKnots, flightType, acType,
- airline));
-
-
- FGAIManager* aimgr = (FGAIManager *) globals-> get_subsystem("ai_model");
- aimgr->attach(aircraft);
- AIManagerRef = aircraft->getID();
- return true;
+ FGAIFlightPlan *fp = new FGAIFlightPlan(aiAircraft, flightPlanName, courseToDest, deptime,
+ dep, arr, true, radius,
+ flight->getCruiseAlt()*100,
+ position.getLatitudeDeg(),
+ position.getLongitudeDeg(),
+ speedKnots, flightType, acType,
+ airline);
+ if (fp->isValidPlan()) {
+ aiAircraft->SetFlightPlan(fp);
+ FGAIManager* aimgr = (FGAIManager *) globals-> get_subsystem("ai-model");
+ aimgr->attach(aiAircraft);
+ return true;
+ } else {
+ aiAircraft = NULL;
+ delete fp;
+ //hand back the flights that had already been scheduled
+ while (!flights.empty()) {
+ flights.front()->release();
+ flights.erase(flights.begin());
+ }
+ return false;
+ }
+}
+
+// Create an initial heading for user controlled aircraft.
+void FGAISchedule::setHeading()
+{
+ courseToDest = SGGeodesy::courseDeg((*flights.begin())->getDepartureAirport()->geod(), (*flights.begin())->getArrivalAirport()->geod());
}
-void FGAISchedule::scheduleFlights()
+bool FGAISchedule::scheduleFlights(time_t now)
{
- if (!flights.empty()) {
- return;
- }
-
- SG_LOG(SG_GENERAL, SG_BULK, "Scheduling for : " << modelPath << " " << registration << " " << homePort);
+ //string startingPort;
+ const string& userPort = fgGetString("/sim/presets/airport-id");
+ SG_LOG(SG_AI, SG_BULK, "Scheduling Flights for : " << modelPath << " " << registration << " " << homePort);
FGScheduledFlight *flight = NULL;
+ SGTimeStamp start;
+ start.stamp();
+
+ bool first = true;
+ if (currentDestination.empty())
+ flight = findAvailableFlight(userPort, flightIdentifier, now, (now+6400));
+
do {
- flight = findAvailableFlight(currentDestination, flightIdentifier);
+ if ((!flight)||(!first)) {
+ flight = findAvailableFlight(currentDestination, flightIdentifier);
+ }
if (!flight) {
break;
}
-
+
+ first = false;
currentDestination = flight->getArrivalAirport()->getId();
+ //cerr << "Current destination " << currentDestination << endl;
+ if (!initialized) {
+ const string& departurePort = flight->getDepartureAirport()->getId();
+ if (userPort == departurePort) {
+ lastRun = 1;
+ hits++;
+ } else {
+ lastRun = 0;
+ }
+ //runCount++;
+ initialized = true;
+ }
- time_t arr, dep;
- dep = flight->getDepartureTime();
- arr = flight->getArrivalTime();
- string depT = asctime(gmtime(&dep));
- string arrT = asctime(gmtime(&arr));
-
- depT = depT.substr(0,24);
- arrT = arrT.substr(0,24);
- SG_LOG(SG_GENERAL, SG_BULK, " " << flight->getCallSign() << ":"
- << " " << flight->getDepartureAirport()->getId() << ":"
- << " " << depT << ":"
- << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
- << " " << arrT << ":");
+ if (sglog().would_log(SG_AI, SG_BULK))
+ {
+ time_t arr, dep;
+ dep = flight->getDepartureTime();
+ arr = flight->getArrivalTime();
+ string depT = asctime(gmtime(&dep));
+ string arrT = asctime(gmtime(&arr));
+ depT = depT.substr(0,24);
+ arrT = arrT.substr(0,24);
+ SG_LOG(SG_AI, SG_BULK, " Flight " << flight->getCallSign() << ":"
+ << " " << flight->getDepartureAirport()->getId() << ":"
+ << " " << depT << ":"
+ << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
+ << " " << arrT << ":");
+ }
flights.push_back(flight);
- } while (currentDestination != homePort);
- SG_LOG(SG_GENERAL, SG_BULK, " Done ");
+
+ // continue processing until complete, or preempt after timeout
+ } while ((currentDestination != homePort)&&
+ (start.elapsedMSec()<3.0));
+
+ if (flight && (currentDestination != homePort))
+ {
+ // processing preempted, need to continue in next iteration
+ return false;
+ }
+
+ SG_LOG(SG_AI, SG_BULK, " Done ");
+ return true;
}
bool FGAISchedule::next()
}
FGScheduledFlight* FGAISchedule::findAvailableFlight (const string ¤tDestination,
- const string &req)
+ const string &req,
+ time_t min, time_t max)
{
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
- FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("Traffic Manager");
+ FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("traffic-manager");
FGScheduledFlightVecIterator fltBegin, fltEnd;
fltBegin = tmgr->getFirstFlight(req);
fltEnd = tmgr->getLastFlight(req);
continue;
}
}
- //TODO: check time
+ if (flights.size()) {
+ time_t arrival = flights.back()->getArrivalTime();
+ int groundTime = groundTimeFromRadius();
+ if ((*i)->getDepartureTime() < (arrival+(groundTime)))
+ continue;
+ }
+ if (min != 0) {
+ time_t dep = (*i)->getDepartureTime();
+ if ((dep < min) || (dep > max))
+ continue;
+ }
+
// So, if we actually get here, we have a winner
//cerr << "found flight: " << req << " : " << currentDestination << " : " <<
// (*i)->getArrivalAirport()->getId() << endl;
return NULL;
}
+int FGAISchedule::groundTimeFromRadius()
+{
+ if (radius < 10)
+ return 15 * 60;
+ else if (radius < 15)
+ return 20 * 60;
+ else if (radius < 20)
+ return 30 * 60;
+ else if (radius < 25)
+ return 50 * 60;
+ else if (radius < 30)
+ return 90 * 60;
+ else
+ return 120 * 60;
+}
+
+
double FGAISchedule::getSpeed()
{
FGScheduledFlightVecIterator i = flights.begin();
SG_CLAMP_RANGE(speed, 300.0, 500.0);
return speed;
}
-/*
+
+void FGAISchedule::setScore ()
+{
+ if (runCount) {
+ score = ((double) hits / (double) runCount);
+ } else {
+ if (homePort == fgGetString("/sim/presets/airport-id")) {
+ score = 0.1;
+ } else {
+ score = 0.0;
+ }
+ }
+ runCount++;
+}
+
bool compareSchedules(FGAISchedule*a, FGAISchedule*b)
{
- //return (*a) < (*b);
+ return (*a) < (*b);
}
-*/
-
-// void FGAISchedule::setClosestDistanceToUser()
-// {
-
-
-// double course;
-// double dist;
-
-// Point3D temp;
-// time_t
-// totalTimeEnroute,
-// elapsedTimeEnroute;
-
-// double userLatitude = fgGetDouble("/position/latitude-deg");
-// double userLongitude = fgGetDouble("/position/longitude-deg");
-// FGAirport *dep;
-
-// #if defined( __CYGWIN__) || defined( __MINGW32__)
-// #define HUGE HUGE_VAL
-// #endif
-// distanceToUser = HUGE;
-// FGScheduledFlightVecIterator i = flights.begin();
-// while (i != flights.end())
-// {
-// dep = i->getDepartureAirport();
-// //if (!(dep))
-// //return HUGE;
-
-// SGWayPoint user ( userLongitude,
-// userLatitude,
-// i->getCruiseAlt());
-// SGWayPoint current (dep->getLongitude(),
-// dep->getLatitude(),
-// 0);
-// user.CourseAndDistance(current, &course, &dist);
-// if (dist < distanceToUser)
-// {
-// distanceToUser = dist;
-// //cerr << "Found closest distance to user for " << registration << " to be " << distanceToUser << " at airport " << dep->getId() << endl;
-// }
-// i++;
-// }
-// //return distToUser;
-// }
+bool FGAISchedule::operator< (const FGAISchedule &other) const
+{
+ //cerr << "Sorting " << registration << " and " << other.registration << endl;
+ double currentScore = score * (1.5 - lastRun);
+ double otherScore = other.score * (1.5 - other.lastRun);
+ return currentScore > otherScore;
+}