]> git.mxchange.org Git - flightgear.git/blobdiff - src/Traffic/Schedule.cxx
Close dialogs on GUI shutdown
[flightgear.git] / src / Traffic / Schedule.cxx
index 3b990e377d3364213c27d6269af5ee5e3a2f28a9..a47dfd2ca3493c2485be2d0b1181736b07747e0d 100644 (file)
 #include <string>
 #include <vector>
 #include <algorithm>
+#include <boost/foreach.hpp>
 
 #include <simgear/compiler.h>
 #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
 
 
 #include "SchedFlight.hxx"
 #include "TrafficMgr.hxx"
 
+using std::string;
+
 /******************************************************************************
  * 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; 
@@ -106,17 +114,10 @@ FGAISchedule::FGAISchedule(string model,
   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)
@@ -128,7 +129,7 @@ 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;
@@ -136,15 +137,29 @@ FGAISchedule::FGAISchedule(const FGAISchedule &other)
   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);
@@ -178,22 +193,37 @@ bool FGAISchedule::init()
   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. 
@@ -210,14 +240,12 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
   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
     }
@@ -226,19 +254,19 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
   // 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;
@@ -246,7 +274,7 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
     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,
@@ -258,19 +286,19 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
       
       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();
   }
@@ -279,18 +307,50 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart)
   // 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)
+{
+    return (resolveModelPath(modelPath) != SGPath());
+}
+
+SGPath FGAISchedule::resolveModelPath(const std::string& modelPath)
+{
+    BOOST_FOREACH(SGPath aiPath, globals->get_data_paths("AI")) {
+        aiPath.append(modelPath);
+        if (aiPath.exists()) {
+            return aiPath;
+        }
+    }
+    
+    // check aircraft dirs
+    BOOST_FOREACH(std::string aircraftPath, globals->get_aircraft_paths()) {
+        SGPath mp(aircraftPath);
+        mp.append(modelPath);
+        if (mp.exists()) {
+            return mp;
+        }
+    }
+
+    return SGPath();
 }
 
 bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime)
@@ -298,82 +358,117 @@ bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots
   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);
-
-  // Only allow traffic to be created when the model path (or the AI version of mp) exists
-  SGPath mp(globals->get_fg_root());
-  SGPath mp_ai = mp;
+  SG_LOG(SG_AI, SG_DEBUG, "Traffic manager: Creating AIModel from:" << flightPlanName);
 
-  mp.append(modelPath);
-  mp_ai.append("AI");
-  mp_ai.append(modelPath);
-
-  if (!mp.exists() && !mp_ai.exists()) {
-    SG_LOG(SG_INPUT, SG_WARN, "TrafficManager: Could not load model " << mp.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()
@@ -409,11 +504,12 @@ bool FGAISchedule::next()
 }
 
 FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDestination,
-                                                      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);
@@ -453,7 +549,18 @@ FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDesti
                    continue;
               }
           }
-          //TODO: check time
+          if (! flights.empty()) {
+            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;
@@ -469,6 +576,23 @@ FGScheduledFlight* FGAISchedule::findAvailableFlight (const string &currentDesti
      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();
@@ -482,55 +606,31 @@ double FGAISchedule::getSpeed()
   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;
+}