first commit
This commit is contained in:
16
src/Traffic/CMakeLists.txt
Normal file
16
src/Traffic/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
SchedFlight.cxx
|
||||
Schedule.cxx
|
||||
TrafficMgr.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
SchedFlight.hxx
|
||||
Schedule.hxx
|
||||
TrafficMgr.hxx
|
||||
)
|
||||
|
||||
|
||||
flightgear_component(Traffic "${SOURCES}" "${HEADERS}")
|
||||
312
src/Traffic/SchedFlight.cxx
Normal file
312
src/Traffic/SchedFlight.cxx
Normal file
@@ -0,0 +1,312 @@
|
||||
/******************************************************************************
|
||||
* SchedFlight.cxx
|
||||
* Written by Durk Talsma, started May 5, 2004.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/* This a prototype version of a top-level flight plan manager for Flightgear.
|
||||
* It parses the fgtraffic.txt file and determine for a specific time/date,
|
||||
* where each aircraft listed in this file is at the current time.
|
||||
*
|
||||
* I'm currently assuming the following simplifications:
|
||||
* 1) The earth is a perfect sphere
|
||||
* 2) Each aircraft flies a perfect great circle route.
|
||||
* 3) Each aircraft flies at a constant speed (with infinite accelerations and
|
||||
* decelerations)
|
||||
* 4) Each aircraft leaves at exactly the departure time.
|
||||
* 5) Each aircraft arrives at exactly the specified arrival time.
|
||||
*
|
||||
* TODO:
|
||||
* - Check the code for known portability issues
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
#include <simgear/xml/easyxml.hxx>
|
||||
|
||||
#include <AIModel/AIFlightPlan.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
|
||||
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "SchedFlight.hxx"
|
||||
|
||||
using std::string;
|
||||
|
||||
/******************************************************************************
|
||||
* FGScheduledFlight stuff
|
||||
*****************************************************************************/
|
||||
|
||||
FGScheduledFlight::FGScheduledFlight()
|
||||
{
|
||||
departureTime = 0;
|
||||
arrivalTime = 0;
|
||||
cruiseAltitude = 0;
|
||||
repeatPeriod = 0;
|
||||
initialized = false;
|
||||
available = true;
|
||||
departurePort = NULL;
|
||||
arrivalPort = NULL;
|
||||
}
|
||||
|
||||
FGScheduledFlight::FGScheduledFlight(const FGScheduledFlight &other)
|
||||
{
|
||||
callsign = other.callsign;
|
||||
fltRules = other.fltRules;
|
||||
departurePort = other.departurePort;
|
||||
depId = other.depId;
|
||||
arrId = other.arrId;
|
||||
departureTime = other.departureTime;
|
||||
cruiseAltitude = other.cruiseAltitude;
|
||||
arrivalPort = other.arrivalPort;
|
||||
arrivalTime = other.arrivalTime;
|
||||
repeatPeriod = other.repeatPeriod;
|
||||
initialized = other.initialized;
|
||||
requiredAircraft = other.requiredAircraft;
|
||||
available = other.available;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cs The callsign
|
||||
* @param fr The flightrules
|
||||
* @param depPrt The departure ICAO
|
||||
* @param arrPrt The arrival ICAO
|
||||
*/
|
||||
|
||||
FGScheduledFlight::FGScheduledFlight(const string& cs,
|
||||
const string& fr,
|
||||
const string& depPrt,
|
||||
const string& arrPrt,
|
||||
int cruiseAlt,
|
||||
const string& deptime,
|
||||
const string& arrtime,
|
||||
const string& rep,
|
||||
const string& reqAC)
|
||||
{
|
||||
callsign = cs;
|
||||
fltRules = fr;
|
||||
//departurePort.setId(depPrt);
|
||||
//arrivalPort.setId(arrPrt);
|
||||
depId = depPrt;
|
||||
arrId = arrPrt;
|
||||
//cerr << "Constructor: departure " << depId << ". arrival " << arrId << endl;
|
||||
//departureTime = processTimeString(deptime);
|
||||
//arrivalTime = processTimeString(arrtime);
|
||||
cruiseAltitude = cruiseAlt;
|
||||
requiredAircraft = reqAC;
|
||||
|
||||
// Process the repeat period string
|
||||
if (rep.find("WEEK",0) != string::npos)
|
||||
{
|
||||
repeatPeriod = 7*24*60*60; // in seconds
|
||||
}
|
||||
else if (rep.find("Hr", 0) != string::npos)
|
||||
{
|
||||
repeatPeriod = 60*60*atoi(rep.substr(0,2).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
repeatPeriod = 365*24*60*60;
|
||||
SG_LOG( SG_AI, SG_ALERT, "Unknown repeat period in flight plan "
|
||||
"of flight '" << cs << "': " << rep );
|
||||
}
|
||||
if (!repeatPeriod) {
|
||||
SG_LOG( SG_AI, SG_ALERT, "Zero repeat period in flight plan "
|
||||
"of flight '" << cs << "': " << rep );
|
||||
available = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// What we still need to do is preprocess the departure and
|
||||
// arrival times.
|
||||
departureTime = processTimeString(deptime);
|
||||
arrivalTime = processTimeString(arrtime);
|
||||
//departureTime += rand() % 300; // Make sure departure times are not limited to 5 minute increments.
|
||||
if (departureTime > arrivalTime)
|
||||
{
|
||||
departureTime -= repeatPeriod;
|
||||
}
|
||||
initialized = false;
|
||||
available = true;
|
||||
}
|
||||
|
||||
|
||||
FGScheduledFlight:: ~FGScheduledFlight()
|
||||
{
|
||||
}
|
||||
|
||||
time_t FGScheduledFlight::processTimeString(const string& theTime)
|
||||
{
|
||||
int timeOffsetInDays = 0;
|
||||
int targetHour;
|
||||
int targetMinute;
|
||||
int targetSecond;
|
||||
|
||||
tm targetTimeDate;
|
||||
SGTime* currTimeDate = globals->get_time_params();
|
||||
|
||||
string timeCopy = theTime;
|
||||
|
||||
|
||||
// okay first split theTime string into
|
||||
// weekday, hour, minute, second;
|
||||
// Check if a week day is specified
|
||||
const auto daySeperatorPos = timeCopy.find("/", 0);
|
||||
if (daySeperatorPos != string::npos) {
|
||||
const int weekday = std::stoi(timeCopy.substr(0, daySeperatorPos));
|
||||
timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
|
||||
timeCopy = theTime.substr(daySeperatorPos + 1);
|
||||
}
|
||||
|
||||
const auto timeTokens = simgear::strutils::split(timeCopy, ":");
|
||||
if (timeTokens.size() != 3) {
|
||||
SG_LOG(SG_AI, SG_DEV_WARN, "FGScheduledFlight: Timestring too short. " << theTime << " Defaulted to now");
|
||||
return currTimeDate->get_cur_time();
|
||||
}
|
||||
|
||||
|
||||
targetHour = std::stoi(timeTokens.at(0));
|
||||
targetMinute = std::stoi(timeTokens.at(1));
|
||||
targetSecond = std::stoi(timeTokens.at(2));
|
||||
targetTimeDate.tm_year = currTimeDate->getGmt()->tm_year;
|
||||
targetTimeDate.tm_mon = currTimeDate->getGmt()->tm_mon;
|
||||
targetTimeDate.tm_mday = currTimeDate->getGmt()->tm_mday;
|
||||
targetTimeDate.tm_hour = targetHour;
|
||||
targetTimeDate.tm_min = targetMinute;
|
||||
targetTimeDate.tm_sec = targetSecond;
|
||||
|
||||
time_t processedTime = sgTimeGetGMT(&targetTimeDate);
|
||||
processedTime += timeOffsetInDays * 24 * 60 * 60;
|
||||
if (processedTime < currTimeDate->get_cur_time()) {
|
||||
processedTime += repeatPeriod;
|
||||
}
|
||||
//tm *temp = currTimeDate->getGmt();
|
||||
//char buffer[512];
|
||||
//sgTimeFormatTime(&targetTimeDate, buffer);
|
||||
//cout << "Scheduled Time " << buffer << endl;
|
||||
//cout << "Time :" << time(NULL) << " SGTime : " << sgTimeGetGMT(temp) << endl;
|
||||
return processedTime;
|
||||
}
|
||||
|
||||
void FGScheduledFlight::update()
|
||||
{
|
||||
departureTime += repeatPeriod;
|
||||
arrivalTime += repeatPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* //FIXME Doesn't have to be an iteration / when sitting at departure why adjust based on arrival
|
||||
*/
|
||||
|
||||
void FGScheduledFlight::adjustTime(time_t now)
|
||||
{
|
||||
// Make sure that the arrival time is in between
|
||||
// the current time and the next repeat period.
|
||||
while ((arrivalTime < now) || (arrivalTime > now + repeatPeriod)) {
|
||||
if (arrivalTime < now) {
|
||||
departureTime += repeatPeriod;
|
||||
arrivalTime += repeatPeriod;
|
||||
SG_LOG(SG_AI, SG_BULK, "Adjusted schedule forward : " << callsign << " " << now << " " << departureTime << " " << arrivalTime);
|
||||
} else if (arrivalTime > now + repeatPeriod) {
|
||||
departureTime -= repeatPeriod;
|
||||
arrivalTime -= repeatPeriod;
|
||||
SG_LOG(SG_AI, SG_BULK, "Adjusted schedule backward : " << callsign << " " << now << " " << departureTime << " " << arrivalTime);
|
||||
} else {
|
||||
SG_LOG(SG_AI, SG_BULK, "Not Adjusted schedule : " << now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FGAirport *FGScheduledFlight::getDepartureAirport()
|
||||
{
|
||||
if (!(initialized))
|
||||
{
|
||||
initializeAirports();
|
||||
}
|
||||
if (initialized)
|
||||
return departurePort;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
FGAirport * FGScheduledFlight::getArrivalAirport ()
|
||||
{
|
||||
if (!(initialized))
|
||||
{
|
||||
initializeAirports();
|
||||
}
|
||||
if (initialized)
|
||||
return arrivalPort;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Upon the first time of requesting airport information
|
||||
// for this scheduled flight, these data need to be
|
||||
// looked up in the main FlightGear database.
|
||||
// Missing or bogus Airport codes are currently ignored,
|
||||
// but we should improve that. The best idea is probably to cancel
|
||||
// this flight entirely by removing it from the schedule, if one
|
||||
// of the airports cannot be found.
|
||||
bool FGScheduledFlight::initializeAirports()
|
||||
{
|
||||
//cerr << "Initializing using : " << depId << " " << arrId << endl;
|
||||
departurePort = FGAirport::findByIdent(depId);
|
||||
if(departurePort == NULL)
|
||||
{
|
||||
SG_LOG( SG_AI, SG_DEBUG, "Traffic manager could not find departure airport : " << depId);
|
||||
return false;
|
||||
}
|
||||
arrivalPort = FGAirport::findByIdent(arrId);
|
||||
if(arrivalPort == NULL)
|
||||
{
|
||||
SG_LOG( SG_AI, SG_DEBUG, "Traffic manager could not find arrival airport : " << arrId);
|
||||
return false;
|
||||
}
|
||||
|
||||
//cerr << "Found : " << departurePort->getId() << endl;
|
||||
//cerr << "Found : " << arrivalPort->getId() << endl;
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compareScheduledFlights(FGScheduledFlight *a, FGScheduledFlight *b)
|
||||
{
|
||||
return (*a) < (*b);
|
||||
};
|
||||
124
src/Traffic/SchedFlight.hxx
Normal file
124
src/Traffic/SchedFlight.hxx
Normal file
@@ -0,0 +1,124 @@
|
||||
/* -*- Mode: C++ -*- *****************************************************
|
||||
* SchedFlight.hxx
|
||||
* Written by Durk Talsma. Started May 5, 2004
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************
|
||||
* ScheduledFlight is a class that is used by FlightGear's Traffic Manager
|
||||
* A scheduled flight can be assigned to a schedule, which can be assigned
|
||||
* to an aircraft. The traffic manager decides for each schedule which
|
||||
* scheduled flight (if any) is currently active. I no scheduled flights
|
||||
* are found active, it tries to position the aircraft associated with this
|
||||
* schedule at departure airport of the next scheduled flight.
|
||||
* The class ScheduledFlight is a software implimentation of this.
|
||||
* In summary, this class stores arrival and departure information, as well
|
||||
* as some administrative data, such as the callsign of this particular
|
||||
* flight (used in future ATC scenarios), under which flight rules the
|
||||
* flight is taking place, as well as a requested initial cruise altitude.
|
||||
* Finally, the class contains a repeat period, wich indicates after how
|
||||
* many seconds a flight should repeat in this schedule (which is usually
|
||||
* after either a day or a week). If this value is zero, this flight won't
|
||||
* repeat.
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _FGSCHEDFLIGHT_HXX_
|
||||
#define _FGSCHEDFLIGHT_HXX_
|
||||
|
||||
|
||||
class FGAirport;
|
||||
|
||||
class FGScheduledFlight
|
||||
{
|
||||
private:
|
||||
std::string callsign;
|
||||
std::string fltRules;
|
||||
FGAirport *departurePort;
|
||||
FGAirport *arrivalPort;
|
||||
std::string depId;
|
||||
std::string arrId;
|
||||
std::string requiredAircraft;
|
||||
time_t departureTime;
|
||||
time_t arrivalTime;
|
||||
time_t repeatPeriod;
|
||||
int cruiseAltitude;
|
||||
|
||||
bool initialized;
|
||||
bool available;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
FGScheduledFlight();
|
||||
FGScheduledFlight(const FGScheduledFlight &other);
|
||||
// FGScheduledFlight(const std::string);
|
||||
FGScheduledFlight(const std::string& cs,
|
||||
const std::string& fr,
|
||||
const std::string& depPrt,
|
||||
const std::string& arrPrt,
|
||||
int cruiseAlt,
|
||||
const std::string& deptime,
|
||||
const std::string& arrtime,
|
||||
const std::string& rep,
|
||||
const std::string& reqAC
|
||||
);
|
||||
~FGScheduledFlight();
|
||||
|
||||
void update();
|
||||
bool initializeAirports();
|
||||
|
||||
void adjustTime(time_t now);
|
||||
|
||||
time_t getDepartureTime() { return departureTime; };
|
||||
time_t getArrivalTime () { return arrivalTime; };
|
||||
|
||||
void setDepartureAirport(const std::string& port) { depId = port; };
|
||||
void setArrivalAirport (const std::string& port) { arrId = port; };
|
||||
FGAirport *getDepartureAirport();
|
||||
FGAirport *getArrivalAirport ();
|
||||
|
||||
int getCruiseAlt() { return cruiseAltitude; };
|
||||
|
||||
bool operator<(const FGScheduledFlight &other) const
|
||||
{
|
||||
return (departureTime < other.departureTime);
|
||||
};
|
||||
const std::string& getFlightRules() { return fltRules; };
|
||||
|
||||
time_t processTimeString(const std::string& time);
|
||||
const std::string& getCallSign() {return callsign; };
|
||||
const std::string& getRequirement() { return requiredAircraft; }
|
||||
|
||||
void lock() { available = false; };
|
||||
void release() { available = true; };
|
||||
|
||||
bool isAvailable() { return available; };
|
||||
|
||||
void setCallSign(const std::string& val) { callsign = val; };
|
||||
void setFlightRules(const std::string& val) { fltRules = val; };
|
||||
};
|
||||
|
||||
typedef std::vector<FGScheduledFlight*> FGScheduledFlightVec;
|
||||
typedef std::vector<FGScheduledFlight*>::iterator FGScheduledFlightVecIterator;
|
||||
|
||||
typedef std::map < std::string, FGScheduledFlightVec > FGScheduledFlightMap;
|
||||
|
||||
bool compareScheduledFlights(FGScheduledFlight *a, FGScheduledFlight *b);
|
||||
|
||||
|
||||
#endif
|
||||
719
src/Traffic/Schedule.cxx
Normal file
719
src/Traffic/Schedule.cxx
Normal file
@@ -0,0 +1,719 @@
|
||||
/******************************************************************************
|
||||
* Schedule.cxx
|
||||
* Written by Durk Talsma, started May 5, 2004.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#define BOGUS 0xFFFF
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/debug/ErrorReportingCallback.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/sg_inlines.h>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
#include <simgear/xml/easyxml.hxx>
|
||||
|
||||
#include <AIModel/AIFlightPlan.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
#include <AIModel/AIAircraft.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#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 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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
FGAISchedule::FGAISchedule(const string& model,
|
||||
const string& lvry,
|
||||
const string& port,
|
||||
const string& reg,
|
||||
const string& flightId,
|
||||
bool hvy,
|
||||
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;
|
||||
homePort = port;
|
||||
registration = reg;
|
||||
flightIdentifier = flightId;
|
||||
acType = act;
|
||||
airline = arln;
|
||||
m_class = mclass;
|
||||
flightType = fltpe;
|
||||
/*for (FGScheduledFlightVecIterator i = flt.begin();
|
||||
i != flt.end();
|
||||
i++)
|
||||
flights.push_back(new FGScheduledFlight((*(*i))));*/
|
||||
}
|
||||
|
||||
FGAISchedule::FGAISchedule(const FGAISchedule &other)
|
||||
{
|
||||
modelPath = other.modelPath;
|
||||
homePort = other.homePort;
|
||||
livery = other.livery;
|
||||
registration = other.registration;
|
||||
heavy = other.heavy;
|
||||
flightIdentifier = other.flightIdentifier;
|
||||
flights = other.flights;
|
||||
aiAircraft = other.aiAircraft;
|
||||
acType = other.acType;
|
||||
airline = other.airline;
|
||||
m_class = other.m_class;
|
||||
firstRun = other.firstRun;
|
||||
radius = other.radius;
|
||||
groundOffset = other.groundOffset;
|
||||
flightType = other.flightType;
|
||||
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);
|
||||
}
|
||||
flights.clear();*/
|
||||
}
|
||||
|
||||
bool FGAISchedule::init()
|
||||
{
|
||||
//tm targetTimeDate;
|
||||
//SGTime* currTimeDate = globals->get_time_params();
|
||||
|
||||
//tm *temp = currTimeDate->getGmt();
|
||||
//char buffer[512];
|
||||
//sgTimeFormatTime(&targetTimeDate, buffer);
|
||||
//cout << "Scheduled Time " << buffer << endl;
|
||||
//cout << "Time :" << time(NULL) << " SGTime : " << sgTimeGetGMT(temp) << endl;
|
||||
/*for (FGScheduledFlightVecIterator i = flights.begin();
|
||||
i != flights.end();
|
||||
i++)
|
||||
{
|
||||
//i->adjustTime(now);
|
||||
if (!((*i)->initializeAirports()))
|
||||
return false;
|
||||
} */
|
||||
//sort(flights.begin(), flights.end());
|
||||
// Since time isn't initialized yet when this function is called,
|
||||
// Find the closest possible airport.
|
||||
// This should give a reasonable initialization order.
|
||||
//setClosestDistanceToUser();
|
||||
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)
|
||||
{
|
||||
|
||||
time_t totalTimeEnroute;
|
||||
time_t elapsedTimeEnroute;
|
||||
time_t remainingTimeEnroute;
|
||||
time_t 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
|
||||
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.
|
||||
//sort(flights.begin(), flights.end(), compareScheduledFlights);
|
||||
|
||||
if (firstRun) {
|
||||
if (fgGetBool("/sim/traffic-manager/instantaneous-action") == true) {
|
||||
deptime = now; // + rand() % 300; // Wait up to 5 minutes until traffic starts moving to prevent too many aircraft
|
||||
// from cluttering the gate areas.
|
||||
}
|
||||
firstRun = false;
|
||||
}
|
||||
|
||||
FGScheduledFlight* flight = flights.front();
|
||||
if (!deptime) {
|
||||
deptime = flight->getDepartureTime();
|
||||
//cerr << "Setting departure time " << deptime << endl;
|
||||
}
|
||||
|
||||
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_AI, SG_BULK, "Traffic Manager: " << flight->getCallSign() << " 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();
|
||||
flights.erase(flights.begin()); // pop_front(), effectively
|
||||
return true; // processing complete
|
||||
}
|
||||
|
||||
FGAirport* dep = flight->getDepartureAirport();
|
||||
FGAirport* arr = flight->getArrivalAirport();
|
||||
if (!dep || !arr) {
|
||||
return true; // processing complete
|
||||
}
|
||||
|
||||
double speed = 450.0;
|
||||
if (dep != arr) {
|
||||
totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime();
|
||||
if (flight->getDepartureTime() < now) {
|
||||
elapsedTimeEnroute = now - flight->getDepartureTime();
|
||||
remainingTimeEnroute = totalTimeEnroute - elapsedTimeEnroute;
|
||||
double x = elapsedTimeEnroute / (double) totalTimeEnroute;
|
||||
|
||||
// current pos is based on great-circle course between departure/arrival,
|
||||
// with percentage of distance travelled, based upon percentage of time
|
||||
// enroute elapsed.
|
||||
double course, az2, distanceM;
|
||||
SGGeodesy::inverse(dep->geod(), arr->geod(), course, az2, distanceM);
|
||||
double coveredDistance = distanceM * x;
|
||||
|
||||
//FIXME very crude that doesn't harmonise with Legs
|
||||
SGGeodesy::direct(dep->geod(), course, coveredDistance, position, az2);
|
||||
|
||||
SG_LOG (SG_AI, SG_BULK, "Traffic Manager: " << flight->getCallSign() << " is in progress " << (x*100) << "%");
|
||||
speed = ((distanceM - coveredDistance) * SG_METER_TO_NM) / 3600.0;
|
||||
} else {
|
||||
// not departed yet
|
||||
remainingTimeEnroute = totalTimeEnroute;
|
||||
elapsedTimeEnroute = 0;
|
||||
position = dep->geod();
|
||||
SG_LOG (SG_AI, SG_BULK, "Traffic Manager: " << flight->getCallSign() << " is pending, departure in "
|
||||
<< flight->getDepartureTime() - now << " seconds ");
|
||||
}
|
||||
} else {
|
||||
// departure / arrival coincident
|
||||
remainingTimeEnroute = totalTimeEnroute = flight->getArrivalTime() - flight->getDepartureTime();
|
||||
elapsedTimeEnroute = 0;
|
||||
position = dep->geod();
|
||||
}
|
||||
|
||||
// cartesian calculations are more numerically stable over the (potentially)
|
||||
// large distances involved here: see bug #80
|
||||
distanceToUser = dist(userCart, SGVec3d::fromGeod(position)) * SG_METER_TO_NM;
|
||||
|
||||
|
||||
// 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_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.
|
||||
}
|
||||
|
||||
if (!createAIAircraft(flight, speed, deptime, remainingTimeEnroute)) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
|
||||
return true; // processing complete
|
||||
}
|
||||
|
||||
bool FGAISchedule::validModelPath(const std::string& modelPath)
|
||||
{
|
||||
return (resolveModelPath(modelPath) != SGPath());
|
||||
}
|
||||
|
||||
SGPath FGAISchedule::resolveModelPath(const std::string& modelPath)
|
||||
{
|
||||
for (auto aiPath : globals->get_data_paths("AI")) {
|
||||
aiPath.append(modelPath);
|
||||
if (aiPath.exists()) {
|
||||
return aiPath;
|
||||
}
|
||||
}
|
||||
|
||||
// check aircraft dirs
|
||||
for (auto aircraftPath : globals->get_aircraft_paths()) {
|
||||
SGPath mp = aircraftPath / modelPath;
|
||||
if (mp.exists()) {
|
||||
return mp;
|
||||
}
|
||||
}
|
||||
|
||||
return SGPath();
|
||||
}
|
||||
|
||||
bool FGAISchedule::createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime, time_t remainingTime)
|
||||
{
|
||||
//FIXME The position must be set here not in update
|
||||
FGAirport* dep = flight->getDepartureAirport();
|
||||
FGAirport* arr = flight->getArrivalAirport();
|
||||
string flightPlanName = dep->getId() + "-" + arr->getId() + ".xml";
|
||||
SG_LOG(SG_AI, SG_DEBUG, flight->getCallSign() << "|Traffic manager: Creating AIModel from:" << flightPlanName);
|
||||
|
||||
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);
|
||||
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());
|
||||
std::unique_ptr<FGAIFlightPlan> fp(new FGAIFlightPlan(aiAircraft,
|
||||
flightPlanName,
|
||||
courseToDest,
|
||||
deptime,
|
||||
remainingTime,
|
||||
dep,
|
||||
arr,
|
||||
true,
|
||||
radius,
|
||||
flight->getCruiseAlt()*100,
|
||||
position.getLatitudeDeg(),
|
||||
position.getLongitudeDeg(),
|
||||
speedKnots, flightType, acType,
|
||||
airline));
|
||||
if (fp->isValidPlan()) {
|
||||
// set this here so it's available inside attach, which calls AIBase::init
|
||||
simgear::ErrorReportContext ec{"traffic-aircraft-callsign", flight->getCallSign()};
|
||||
|
||||
aiAircraft->FGAIBase::setFlightPlan(std::move(fp));
|
||||
globals->get_subsystem<FGAIManager>()->attach(aiAircraft);
|
||||
if (aiAircraft->_getProps()) {
|
||||
SGPropertyNode* nodeForAircraft = aiAircraft->_getProps();
|
||||
if (dep) {
|
||||
nodeForAircraft->getChild("departure-airport-id", 0, true)->setStringValue(dep->getId());
|
||||
nodeForAircraft->getChild("departure-time-sec", 0, true)->setIntValue(deptime);
|
||||
}
|
||||
if (arr) {
|
||||
nodeForAircraft->getChild("arrival-airport-id", 0, true)->setStringValue(arr->getId());
|
||||
// arrival time not known here
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
aiAircraft = NULL;
|
||||
//hand back the flights that had already been scheduled
|
||||
while (!flights.empty()) {
|
||||
flights.front()->release();
|
||||
flights.erase(flights.begin());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FGAISchedule::setHeading()
|
||||
{
|
||||
courseToDest = SGGeodesy::courseDeg((*flights.begin())->getDepartureAirport()->geod(), (*flights.begin())->getArrivalAirport()->geod());
|
||||
}
|
||||
|
||||
void FGAISchedule::assign(FGScheduledFlight *ref) { flights.push_back(ref); }
|
||||
|
||||
/**
|
||||
Warning - will empty the flights vector no matter what. Use with caution!
|
||||
*/
|
||||
void FGAISchedule::clearAllFlights() { flights.clear(); }
|
||||
|
||||
bool FGAISchedule::scheduleFlights(time_t now)
|
||||
{
|
||||
//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 {
|
||||
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_AI, SG_BULK, " Flight " << flight->getCallSign() << ":"
|
||||
<< " " << flight->getDepartureAirport()->getId() << ":"
|
||||
<< " " << depT << ":"
|
||||
<< " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
|
||||
<< " " << arrT << ":");
|
||||
|
||||
flights.push_back(flight);
|
||||
|
||||
// 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()
|
||||
{
|
||||
if (!flights.empty()) {
|
||||
flights.front()->release();
|
||||
flights.erase(flights.begin());
|
||||
}
|
||||
|
||||
FGScheduledFlight *flight = findAvailableFlight(currentDestination, flightIdentifier);
|
||||
if (!flight) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentDestination = flight->getArrivalAirport()->getId();
|
||||
/*
|
||||
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);
|
||||
//cerr << " " << flight->getCallSign() << ":"
|
||||
// << " " << flight->getDepartureAirport()->getId() << ":"
|
||||
// << " " << depT << ":"
|
||||
// << " \"" << flight->getArrivalAirport()->getId() << "\"" << ":"
|
||||
// << " " << arrT << ":" << endl;
|
||||
*/
|
||||
flights.push_back(flight);
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t FGAISchedule::getDepartureTime()
|
||||
{
|
||||
if (flights.empty())
|
||||
return 0;
|
||||
|
||||
return (*flights.begin())->getDepartureTime ();
|
||||
}
|
||||
|
||||
FGAirport *FGAISchedule::getDepartureAirport()
|
||||
{
|
||||
if (flights.empty())
|
||||
return 0;
|
||||
|
||||
return (*flights.begin())->getDepartureAirport();
|
||||
}
|
||||
|
||||
FGAirport *FGAISchedule::getArrivalAirport()
|
||||
{
|
||||
if (flights.empty())
|
||||
return 0;
|
||||
|
||||
return (*flights.begin())->getArrivalAirport ();
|
||||
}
|
||||
|
||||
int FGAISchedule::getCruiseAlt()
|
||||
{
|
||||
if (flights.empty())
|
||||
return 0;
|
||||
|
||||
return (*flights.begin())->getCruiseAlt ();
|
||||
}
|
||||
|
||||
std::string FGAISchedule::getCallSign()
|
||||
{
|
||||
if (flights.empty())
|
||||
return std::string();
|
||||
|
||||
return (*flights.begin())->getCallSign ();
|
||||
}
|
||||
|
||||
std::string FGAISchedule::getFlightRules()
|
||||
{
|
||||
if (flights.empty())
|
||||
return std::string();
|
||||
|
||||
return (*flights.begin())->getFlightRules ();
|
||||
}
|
||||
|
||||
FGScheduledFlight* FGAISchedule::findAvailableFlight (const string ¤tDestination,
|
||||
const string &req,
|
||||
time_t min, time_t max)
|
||||
{
|
||||
time_t now = globals->get_time_params()->get_cur_time();
|
||||
|
||||
FGTrafficManager *tmgr = (FGTrafficManager *) globals->get_subsystem("traffic-manager");
|
||||
FGScheduledFlightVecIterator fltBegin, fltEnd;
|
||||
fltBegin = tmgr->getFirstFlight(req);
|
||||
fltEnd = tmgr->getLastFlight(req);
|
||||
|
||||
|
||||
SG_LOG (SG_AI, SG_BULK, "Finding available flight for " << req << " at " << now);
|
||||
// For Now:
|
||||
// Traverse every registered flight
|
||||
if (fltBegin == fltEnd) {
|
||||
SG_LOG (SG_AI, SG_BULK, "No Flights Scheduled for " << req );
|
||||
}
|
||||
int counter = 0;
|
||||
for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
|
||||
(*i)->adjustTime(now);
|
||||
//sort(fltBegin, fltEnd, compareScheduledFlights);
|
||||
//cerr << counter++ << endl;
|
||||
}
|
||||
std::sort(fltBegin, fltEnd, compareScheduledFlights);
|
||||
for (FGScheduledFlightVecIterator i = fltBegin; i != fltEnd; i++) {
|
||||
//bool valid = true;
|
||||
counter++;
|
||||
if (!(*i)->isAvailable()) {
|
||||
SG_LOG(SG_AI, SG_BULK, "" << (*i)->getCallSign() << "is no longer available");
|
||||
|
||||
//cerr << (*i)->getCallSign() << "is no longer available" << endl;
|
||||
continue;
|
||||
}
|
||||
if (!((*i)->getRequirement() == req)) {
|
||||
SG_LOG(SG_AI, SG_BULK, "" << (*i)->getCallSign() << " no requirement " << (*i)->getRequirement() << " " << req);
|
||||
continue;
|
||||
}
|
||||
if (!(((*i)->getArrivalAirport()) && ((*i)->getDepartureAirport()))) {
|
||||
continue;
|
||||
}
|
||||
if (!(currentDestination.empty())) {
|
||||
if (currentDestination != (*i)->getDepartureAirport()->getId()) {
|
||||
SG_LOG(SG_AI, SG_BULK, (*i)->getCallSign() << " not matching departure.");
|
||||
//cerr << (*i)->getCallSign() << "Doesn't match destination" << endl;
|
||||
//cerr << "Current Destination " << currentDestination << "Doesnt match flight's " <<
|
||||
// (*i)->getArrivalAirport()->getId() << endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!flights.empty()) {
|
||||
time_t arrival = flights.back()->getArrivalTime();
|
||||
time_t departure = (*i)->getDepartureTime();
|
||||
int groundTime = groundTimeFromRadius();
|
||||
if (departure < (arrival+(groundTime))) {
|
||||
SG_LOG (SG_AI, SG_BULK, "Not flight candidate : " << (*i)->getCallSign() << " Flight Arrival : " << arrival << " Planned Departure : " << departure << " < " << (arrival+groundTime) << " Diff between arrival + groundtime and departure : " << (arrival+groundTime-departure) << " Groundtime : " << groundTime);
|
||||
continue;
|
||||
} else {
|
||||
SG_LOG (SG_AI, SG_BULK, "Next flight candidate : " << (*i)->getCallSign() );
|
||||
}
|
||||
}
|
||||
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;
|
||||
(*i)->lock();
|
||||
return (*i);
|
||||
}
|
||||
// matches req?
|
||||
// if currentDestination has a value, does it match departure of next flight?
|
||||
// is departure time later than planned arrival?
|
||||
// is departure port valid?
|
||||
// is arrival port valid?
|
||||
//cerr << "Ack no flight found: " << 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();
|
||||
|
||||
FGAirport* dep = (*i)->getDepartureAirport(),
|
||||
*arr = (*i)->getArrivalAirport();
|
||||
double dist = SGGeodesy::distanceNm(dep->geod(), arr->geod());
|
||||
double remainingTimeEnroute = (*i)->getArrivalTime() - (*i)->getDepartureTime();
|
||||
|
||||
double speed = 0.0;
|
||||
if (remainingTimeEnroute > 0.01)
|
||||
speed = dist / (remainingTimeEnroute/3600.0);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
155
src/Traffic/Schedule.hxx
Normal file
155
src/Traffic/Schedule.hxx
Normal file
@@ -0,0 +1,155 @@
|
||||
/* -*- Mode: C++ -*- *****************************************************
|
||||
* Schedule.hxx
|
||||
* Written by Durk Talsma. Started May 5, 2004
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************
|
||||
* This file contains the definition of the class Schedule.
|
||||
*
|
||||
* A schedule is basically a number of scheduled flights, which can be
|
||||
* assigned to an AI aircraft.
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _FGSCHEDULE_HXX_
|
||||
#define _FGSCHEDULE_HXX_
|
||||
|
||||
#define TRAFFICTOAIDISTTOSTART 150.0
|
||||
#define TRAFFICTOAIDISTTODIE 200.0
|
||||
|
||||
// forward decls
|
||||
class FGAIAircraft;
|
||||
class FGScheduledFlight;
|
||||
|
||||
typedef std::vector<FGScheduledFlight*> FGScheduledFlightVec;
|
||||
|
||||
class FGAISchedule
|
||||
{
|
||||
private:
|
||||
std::string modelPath;
|
||||
std::string homePort;
|
||||
std::string livery;
|
||||
std::string registration;
|
||||
std::string airline;
|
||||
std::string acType;
|
||||
std::string m_class;
|
||||
std::string flightType;
|
||||
std::string flightIdentifier;
|
||||
std::string currentDestination;
|
||||
bool heavy;
|
||||
FGScheduledFlightVec flights;
|
||||
SGGeod position;
|
||||
double radius;
|
||||
double groundOffset;
|
||||
double distanceToUser;
|
||||
double score;
|
||||
unsigned int runCount;
|
||||
unsigned int hits;
|
||||
unsigned int lastRun;
|
||||
bool firstRun;
|
||||
double courseToDest;
|
||||
bool initialized;
|
||||
bool valid;
|
||||
bool scheduleComplete;
|
||||
|
||||
bool scheduleFlights(time_t now);
|
||||
int groundTimeFromRadius();
|
||||
|
||||
/**
|
||||
* Transition this schedule from distant mode to AI mode;
|
||||
* create the AIAircraft (and flight plan) and register with the AIManager
|
||||
*/
|
||||
bool createAIAircraft(FGScheduledFlight* flight, double speedKnots, time_t deptime, time_t remainingTime);
|
||||
|
||||
// the aiAircraft associated with us
|
||||
SGSharedPtr<FGAIAircraft> aiAircraft;
|
||||
public:
|
||||
FGAISchedule(); // constructor
|
||||
FGAISchedule(const std::string& model,
|
||||
const std::string& livery,
|
||||
const std::string& homePort,
|
||||
const std::string& registration,
|
||||
const std::string& flightId,
|
||||
bool heavy,
|
||||
const std::string& acType,
|
||||
const std::string& airline,
|
||||
const std::string& m_class,
|
||||
const std::string& flight_type,
|
||||
double radius,
|
||||
double offset); // construct & init
|
||||
FGAISchedule(const FGAISchedule &other); // copy constructor
|
||||
|
||||
|
||||
|
||||
~FGAISchedule(); //destructor
|
||||
|
||||
static bool validModelPath(const std::string& model);
|
||||
static SGPath resolveModelPath(const std::string& model);
|
||||
|
||||
bool update(time_t now, const SGVec3d& userCart);
|
||||
bool init();
|
||||
|
||||
double getSpeed ();
|
||||
//void setClosestDistanceToUser();
|
||||
bool next(); // forces the schedule to move on to the next flight.
|
||||
|
||||
// TODO: rework these four functions
|
||||
time_t getDepartureTime ();
|
||||
FGAirport * getDepartureAirport ();
|
||||
FGAirport * getArrivalAirport ();
|
||||
int getCruiseAlt ();
|
||||
double getRadius () { return radius; };
|
||||
double getGroundOffset () { return groundOffset;};
|
||||
const std::string& getFlightType () { return flightType;};
|
||||
const std::string& getAirline () { return airline; };
|
||||
const std::string& getAircraft () { return acType; };
|
||||
std::string getCallSign ();
|
||||
const std::string& getRegistration () { return registration;};
|
||||
std::string getFlightRules ();
|
||||
bool getHeavy () { return heavy; };
|
||||
double getCourse () { return courseToDest; };
|
||||
unsigned int getRunCount () { return runCount; };
|
||||
unsigned int getHits () { return hits; };
|
||||
|
||||
void setrunCount(unsigned int count) { runCount = count; };
|
||||
void setHits (unsigned int count) { hits = count; };
|
||||
void setScore ();
|
||||
double getScore () { return score; };
|
||||
/**Create an initial heading for user controlled aircraft.*/
|
||||
void setHeading ();
|
||||
void assign (FGScheduledFlight *ref);
|
||||
void clearAllFlights();
|
||||
void setFlightType (const std::string& val) { flightType = val; };
|
||||
FGScheduledFlight*findAvailableFlight (const std::string& currentDestination, const std::string &req, time_t min=0, time_t max=0);
|
||||
// used to sort in descending order of score: I've probably found a better way to
|
||||
// descending order sorting, but still need to test that.
|
||||
bool operator< (const FGAISchedule &other) const;
|
||||
int getLastUsed() { return lastRun; };
|
||||
void setLastUsed(unsigned int val) {lastRun = val; };
|
||||
//void * getAiRef () { return AIManagerRef; };
|
||||
//FGAISchedule* getAddress () { return this;};
|
||||
|
||||
};
|
||||
|
||||
typedef std::vector<FGAISchedule*> ScheduleVector;
|
||||
typedef std::vector<FGAISchedule*>::iterator ScheduleVectorIterator;
|
||||
|
||||
bool compareSchedules(FGAISchedule*a, FGAISchedule*b);
|
||||
|
||||
#endif
|
||||
|
||||
952
src/Traffic/TrafficMgr.cxx
Normal file
952
src/Traffic/TrafficMgr.cxx
Normal file
@@ -0,0 +1,952 @@
|
||||
/******************************************************************************
|
||||
* TrafficMGr.cxx
|
||||
* Written by Durk Talsma, started May 5, 2004.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/*
|
||||
* Traffic manager parses airlines timetable-like data and uses this to
|
||||
* determine the approximate position of each AI aircraft in its database.
|
||||
* When an AI aircraft is close to the user's position, a more detailed
|
||||
* AIModels based simulation is set up.
|
||||
*
|
||||
* I'm currently assuming the following simplifications:
|
||||
* 1) The earth is a perfect sphere
|
||||
* 2) Each aircraft flies a perfect great circle route.
|
||||
* 3) Each aircraft flies at a constant speed (with infinite accelerations and
|
||||
* decelerations)
|
||||
* 4) Each aircraft leaves at exactly the departure time.
|
||||
* 5) Each aircraft arrives at exactly the specified arrival time.
|
||||
*
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/debug/ErrorReportingCallback.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/threads/SGThread.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
|
||||
#include <simgear/xml/easyxml.hxx>
|
||||
#include <simgear/scene/tsync/terrasync.hxx>
|
||||
|
||||
#include <AIModel/AIAircraft.hxx>
|
||||
#include <AIModel/AIFlightPlan.hxx>
|
||||
#include <AIModel/AIBase.hxx>
|
||||
#include <AIModel/performancedb.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Main/fg_init.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/sentryIntegration.hxx>
|
||||
|
||||
#include "TrafficMgr.hxx"
|
||||
|
||||
using std::sort;
|
||||
using std::strcmp;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
/**
|
||||
* Thread encapsulating parsing the traffic schedules.
|
||||
*/
|
||||
class ScheduleParseThread : public SGThread, public XMLVisitor
|
||||
{
|
||||
public:
|
||||
ScheduleParseThread(FGTrafficManager* traffic) :
|
||||
_trafficManager(traffic),
|
||||
_isFinished(false),
|
||||
_cancelThread(false),
|
||||
cruiseAlt(0),
|
||||
score(0),
|
||||
acCounter(0),
|
||||
radius(0),
|
||||
offset(0),
|
||||
heavy(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// if we're destroyed while running, ensure the thread exits cleanly
|
||||
~ScheduleParseThread()
|
||||
{
|
||||
_lock.lock();
|
||||
if (!_isFinished) {
|
||||
_cancelThread = true; // request cancellation so we don't wait ages
|
||||
_lock.unlock();
|
||||
join();
|
||||
} else {
|
||||
_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void setTrafficDirs(const PathList& dirs)
|
||||
{
|
||||
_trafficDirPaths = dirs;
|
||||
}
|
||||
|
||||
bool isFinished() const
|
||||
{
|
||||
std::lock_guard<std::mutex> g(_lock);
|
||||
return _isFinished;
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
for (const auto& p : _trafficDirPaths) {
|
||||
parseTrafficDir(p);
|
||||
if (_cancelThread) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> g(_lock);
|
||||
_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, *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_BULK, "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:
|
||||
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);
|
||||
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::AITrafficSchedule, "Missing traffic model path:" + mdl, _currentFile);
|
||||
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);
|
||||
|
||||
simgear::ErrorReportContext("ai-traffic-dir", path.utf8Str());
|
||||
|
||||
for (const auto& p : d) {
|
||||
simgear::Dir d2(p);
|
||||
SG_LOG(SG_AI, SG_DEBUG, "parsing traffic in:" << p);
|
||||
simgear::PathList trafficFiles = d2.children(simgear::Dir::TYPE_FILE, ".xml");
|
||||
for (const auto& xml : trafficFiles) {
|
||||
_currentFile = xml;
|
||||
try {
|
||||
readXML(xml, *this);
|
||||
if (_cancelThread) {
|
||||
return;
|
||||
}
|
||||
} catch (sg_exception& e) {
|
||||
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::AITrafficSchedule,
|
||||
"XML errors parsinng traffic:" + e.getFormattedMessage(), xml);
|
||||
}
|
||||
}
|
||||
} // of sub-directories iteration
|
||||
|
||||
SG_LOG(SG_AI, SG_INFO, "parsing traffic schedules took:" << st.elapsedMSec() << "msec");
|
||||
}
|
||||
|
||||
FGTrafficManager* _trafficManager;
|
||||
mutable std::mutex _lock;
|
||||
bool _isFinished;
|
||||
bool _cancelThread;
|
||||
simgear::PathList _trafficDirPaths;
|
||||
SGPath _currentFile;
|
||||
|
||||
// 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;
|
||||
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* TrafficManager
|
||||
*****************************************************************************/
|
||||
FGTrafficManager::FGTrafficManager() :
|
||||
inited(false),
|
||||
doingInit(false),
|
||||
trafficSyncRequested(false),
|
||||
waitingMetarTime(0.0),
|
||||
enabled("/sim/traffic-manager/enabled"),
|
||||
aiEnabled("/sim/ai/enabled"),
|
||||
realWxEnabled("/environment/realwx/enabled"),
|
||||
metarValid("/environment/metar/valid"),
|
||||
active("/sim/traffic-manager/active"),
|
||||
aiDataUpdateNow("/sim/terrasync/ai-data-update-now")
|
||||
{
|
||||
}
|
||||
|
||||
FGTrafficManager::~FGTrafficManager()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void FGTrafficManager::shutdown()
|
||||
{
|
||||
if (!inited) {
|
||||
if (doingInit) {
|
||||
scheduleParser.reset();
|
||||
doingInit = false;
|
||||
active = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the heuristics data
|
||||
bool saveData = false;
|
||||
sg_ofstream cachefile;
|
||||
if (fgGetBool("/sim/traffic-manager/heuristics")) {
|
||||
SGPath cacheData(globals->get_fg_home());
|
||||
cacheData.append("ai");
|
||||
const string airport = fgGetString("/sim/presets/airport-id");
|
||||
|
||||
if ((airport) != "") {
|
||||
char buffer[128];
|
||||
::snprintf(buffer, 128, "%c/%c/%c/",
|
||||
airport[0], airport[1], airport[2]);
|
||||
cacheData.append(buffer);
|
||||
cacheData.append(airport + "-cache.txt");
|
||||
|
||||
// Note: Intuitively, this doesn't make sense, but I do need to create the full file path first
|
||||
// before creating the directories. The SimGear fgpath code has changed so that it first chops off
|
||||
// the trailing dir separator and then determines the directory part of the file path by searching
|
||||
// for the last dir separator. Effecively, this causes a full element of the directory tree to be
|
||||
// skipped.
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Trying to create dir for : " << cacheData);
|
||||
if (!cacheData.exists()) {
|
||||
cacheData.create_dir(0755);
|
||||
}
|
||||
saveData = true;
|
||||
cachefile.open(cacheData);
|
||||
cachefile << "[TrafficManagerCachedata:ref:2011:09:04]" << endl;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (auto acft : scheduledAircraft) {
|
||||
if (saveData) {
|
||||
cachefile << acft->getRegistration() << " "
|
||||
<< acft->getRunCount() << " "
|
||||
<< acft->getHits() << " "
|
||||
<< acft->getLastUsed() << endl;
|
||||
}
|
||||
delete acft;
|
||||
}
|
||||
if (saveData) {
|
||||
cachefile.close();
|
||||
}
|
||||
scheduledAircraft.clear();
|
||||
|
||||
for (auto flight : flights) {
|
||||
for (auto scheduled : flight.second)
|
||||
delete scheduled;
|
||||
}
|
||||
flights.clear();
|
||||
|
||||
currAircraft = scheduledAircraft.begin();
|
||||
doingInit = false;
|
||||
inited = false;
|
||||
trafficSyncRequested = false;
|
||||
active = false;
|
||||
}
|
||||
|
||||
bool FGTrafficManager::doDataSync()
|
||||
{
|
||||
auto terraSync = globals->get_subsystem<simgear::SGTerraSync>();
|
||||
bool doDataSync = fgGetBool("/sim/terrasync/ai-data-enabled");
|
||||
if (doDataSync && terraSync) {
|
||||
if (!trafficSyncRequested) {
|
||||
SG_LOG(SG_AI, SG_INFO, "Sync of AI traffic via TerraSync enabled");
|
||||
terraSync->scheduleDataDir("AI/Traffic");
|
||||
trafficSyncRequested = true;
|
||||
}
|
||||
|
||||
if (terraSync->isDataDirPending("AI/Traffic")) {
|
||||
return false; // remain in the init state
|
||||
}
|
||||
|
||||
trafficSyncRequested = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGTrafficManager::init()
|
||||
{
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TorstenD: don't start the traffic manager before the FDM is initialized
|
||||
// The FDM needs the scenery loaded and will wait for our spawned AIModels PagedLOD Nodes
|
||||
// to appear if they are close (less than 1000m) to our position
|
||||
if( !fgGetBool("/sim/signals/fdm-initialized") )
|
||||
return;
|
||||
|
||||
assert(!doingInit);
|
||||
|
||||
if (!doDataSync())
|
||||
return; // remain in the init state whilst updating
|
||||
|
||||
doingInit = true;
|
||||
if (string(fgGetString("/sim/traffic-manager/datafile")).empty()) {
|
||||
simgear::PathList dirs = globals->get_data_paths("AI/Traffic");
|
||||
|
||||
// temporary flag to restrict loading while traffic data is found
|
||||
// through terrasync /and/ fgdata. Ultimately we *do* want to be able to
|
||||
// overlay sources.
|
||||
|
||||
if (dirs.size() > 1) {
|
||||
SGPath p = dirs.back();
|
||||
if (simgear::strutils::starts_with(p.utf8Str(),
|
||||
globals->get_fg_root().utf8Str()))
|
||||
{
|
||||
dirs.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
if (dirs.empty()) {
|
||||
doingInit = false;
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleParser.reset(new ScheduleParseThread(this));
|
||||
scheduleParser->setTrafficDirs(dirs);
|
||||
scheduleParser->start();
|
||||
} else {
|
||||
fgSetBool("/sim/traffic-manager/heuristics", false);
|
||||
SGPath path = string(fgGetString("/sim/traffic-manager/datafile"));
|
||||
string ext = path.extension();
|
||||
if (path.extension() == "xml") {
|
||||
if (path.exists()) {
|
||||
// use a SchedulerParser to parse, but run it in this thread,
|
||||
// i.e don't start it
|
||||
ScheduleParseThread parser(this);
|
||||
readXML(path, parser);
|
||||
}
|
||||
} else if (path.extension() == "conf") {
|
||||
if (path.exists()) {
|
||||
readTimeTableFromFile(path);
|
||||
}
|
||||
} else {
|
||||
SG_LOG(SG_AI, SG_ALERT,
|
||||
"Unknown data format " << path
|
||||
<< " for traffic");
|
||||
}
|
||||
//exit(1);
|
||||
}
|
||||
active = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGTrafficManager::finishInit()
|
||||
{
|
||||
assert(doingInit);
|
||||
SG_LOG(SG_AI, SG_INFO, "finishing AI-Traffic init");
|
||||
loadHeuristics();
|
||||
PerformanceDB* perfDB = globals->get_subsystem<PerformanceDB>();
|
||||
// Do sorting and scoring separately, to take advantage of the "homeport" variable
|
||||
for (auto schedule : scheduledAircraft) {
|
||||
schedule->setScore();
|
||||
if (!perfDB->havePerformanceDataForAircraftType(schedule->getAircraft())) {
|
||||
SG_LOG(SG_AI, SG_DEV_WARN, "AI-Traffic: schedule aircraft missing performance data:" << schedule->getAircraft());
|
||||
}
|
||||
}
|
||||
|
||||
sort(scheduledAircraft.begin(), scheduledAircraft.end(),
|
||||
compareSchedules);
|
||||
currAircraft = scheduledAircraft.begin();
|
||||
currAircraftClosest = scheduledAircraft.begin();
|
||||
|
||||
doingInit = false;
|
||||
inited = true;
|
||||
active = true;
|
||||
}
|
||||
|
||||
void FGTrafficManager::loadHeuristics()
|
||||
{
|
||||
if (!fgGetBool("/sim/traffic-manager/heuristics")) {
|
||||
return;
|
||||
}
|
||||
|
||||
HeuristicMap heurMap;
|
||||
//cerr << "Processing Heuristics" << endl;
|
||||
// Load the heuristics data
|
||||
SGPath cacheData(globals->get_fg_home());
|
||||
cacheData.append("ai");
|
||||
string airport = fgGetString("/sim/presets/airport-id");
|
||||
if ((airport) != "") {
|
||||
char buffer[128];
|
||||
::snprintf(buffer, 128, "%c/%c/%c/",
|
||||
airport[0], airport[1], airport[2]);
|
||||
cacheData.append(buffer);
|
||||
cacheData.append(airport + "-cache.txt");
|
||||
string revisionStr;
|
||||
if (cacheData.exists()) {
|
||||
sg_ifstream data(cacheData);
|
||||
data >> revisionStr;
|
||||
if (revisionStr != "[TrafficManagerCachedata:ref:2011:09:04]") {
|
||||
SG_LOG(SG_AI, SG_ALERT,"Traffic Manager Warning: discarding outdated cachefile " <<
|
||||
cacheData << " for Airport " << airport);
|
||||
} else {
|
||||
while (1) {
|
||||
Heuristic h; // = new Heuristic;
|
||||
data >> h.registration >> h.runCount >> h.hits >> h.lastRun;
|
||||
if (data.eof())
|
||||
break;
|
||||
HeuristicMapIterator itr = heurMap.find(h.registration);
|
||||
if (itr != heurMap.end()) {
|
||||
SG_LOG(SG_AI, SG_DEV_WARN,"Traffic Manager Warning: found duplicate tailnumber " <<
|
||||
h.registration << " for AI aircraft");
|
||||
} else {
|
||||
heurMap[h.registration] = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(currAircraft = scheduledAircraft.begin(); currAircraft != scheduledAircraft.end(); ++currAircraft) {
|
||||
const string& registration = (*currAircraft)->getRegistration();
|
||||
HeuristicMapIterator itr = heurMap.find(registration);
|
||||
if (itr != heurMap.end()) {
|
||||
(*currAircraft)->setrunCount(itr->second.runCount);
|
||||
(*currAircraft)->setHits(itr->second.hits);
|
||||
(*currAircraft)->setLastUsed(itr->second.lastRun);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FGTrafficManager::metarReady(double dt)
|
||||
{
|
||||
// wait for valid METAR (when realWX is enabled only), since we need
|
||||
// to know the active runway
|
||||
if (metarValid || !realWxEnabled)
|
||||
{
|
||||
waitingMetarTime = 0.0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// METAR timeout: when running offline, remote server is down etc
|
||||
if (waitingMetarStation != fgGetString("/environment/metar/station-id"))
|
||||
{
|
||||
// station has changed: wait for reply, restart timeout
|
||||
waitingMetarTime = 0.0;
|
||||
waitingMetarStation = fgGetString("/environment/metar/station-id");
|
||||
return false;
|
||||
}
|
||||
|
||||
// timeout elapsed (10 seconds)?
|
||||
if (waitingMetarTime > 20.0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
waitingMetarTime += dt;
|
||||
return false;
|
||||
}
|
||||
|
||||
void FGTrafficManager::update(double dt)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
if (inited || doingInit)
|
||||
shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!metarReady(dt))
|
||||
return;
|
||||
|
||||
if (aiDataUpdateNow)
|
||||
{
|
||||
aiDataUpdateNow = false;
|
||||
shutdown();
|
||||
}
|
||||
if (!aiEnabled)
|
||||
{
|
||||
// traffic depends on AI module
|
||||
aiEnabled = true;
|
||||
}
|
||||
|
||||
if (!inited) {
|
||||
if (!doingInit) {
|
||||
init();
|
||||
}
|
||||
|
||||
if (!doingInit || !scheduleParser->isFinished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
finishInit();
|
||||
}
|
||||
|
||||
|
||||
if (scheduledAircraft.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SGVec3d userCart = globals->get_aircraft_position_cart();
|
||||
|
||||
if (currAircraft == scheduledAircraft.end()) {
|
||||
currAircraft = scheduledAircraft.begin();
|
||||
}
|
||||
|
||||
time_t now = globals->get_time_params()->get_cur_time();
|
||||
|
||||
//cerr << "Processing << " << (*currAircraft)->getRegistration() << " with score " << (*currAircraft)->getScore() << endl;
|
||||
if ((*currAircraft)->update(now, userCart)) {
|
||||
// schedule is done - process another aircraft in next iteration
|
||||
currAircraft++;
|
||||
}
|
||||
}
|
||||
|
||||
void FGTrafficManager::readTimeTableFromFile(SGPath infileName)
|
||||
{
|
||||
string model;
|
||||
string livery;
|
||||
string homePort;
|
||||
string registration;
|
||||
string flightReq;
|
||||
bool isHeavy;
|
||||
string acType;
|
||||
string airline;
|
||||
string m_class;
|
||||
string FlightType;
|
||||
double radius;
|
||||
double offset;
|
||||
|
||||
char buffer[256];
|
||||
string buffString;
|
||||
vector <string> tokens, depTime,arrTime;
|
||||
|
||||
sg_ifstream infile(infileName);
|
||||
while (1) {
|
||||
infile.getline(buffer, 256);
|
||||
if (infile.eof()) {
|
||||
break;
|
||||
}
|
||||
//cerr << "Read line : " << buffer << endl;
|
||||
buffString = string(buffer);
|
||||
tokens.clear();
|
||||
Tokenize(buffString, tokens, " \t");
|
||||
//for (it = tokens.begin(); it != tokens.end(); it++) {
|
||||
// cerr << "Tokens: " << *(it) << endl;
|
||||
//}
|
||||
//cerr << endl;
|
||||
if (!tokens.empty()) {
|
||||
if (tokens[0] == string("AC")) {
|
||||
if (tokens.size() != 13) {
|
||||
throw sg_io_exception("Error parsing traffic file @ " + buffString, infileName);
|
||||
}
|
||||
|
||||
|
||||
model = tokens[12];
|
||||
livery = tokens[6];
|
||||
homePort = tokens[1];
|
||||
registration = tokens[2];
|
||||
if (tokens[11] == string("false")) {
|
||||
isHeavy = false;
|
||||
} else {
|
||||
isHeavy = true;
|
||||
}
|
||||
acType = tokens[4];
|
||||
airline = tokens[5];
|
||||
flightReq = tokens[3] + tokens[5];
|
||||
m_class = tokens[10];
|
||||
FlightType = tokens[9];
|
||||
radius = atof(tokens[8].c_str());
|
||||
offset = atof(tokens[7].c_str());;
|
||||
|
||||
if (!FGAISchedule::validModelPath(model)) {
|
||||
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::AITrafficSchedule, "Missing traffic model path:" + model, infileName);
|
||||
} else {
|
||||
SG_LOG(SG_AI, SG_DEBUG, "Adding Aircraft" << model << " " << livery << " " << homePort << " " << registration << " " << flightReq << " " << isHeavy << " " << acType << " " << airline << " " << m_class << " " << FlightType << " " << radius << " " << offset);
|
||||
scheduledAircraft.push_back(new FGAISchedule(model,
|
||||
livery,
|
||||
homePort,
|
||||
registration,
|
||||
flightReq,
|
||||
isHeavy,
|
||||
acType,
|
||||
airline,
|
||||
m_class,
|
||||
FlightType,
|
||||
radius,
|
||||
offset));
|
||||
} // of valid model path
|
||||
}
|
||||
if (tokens[0] == string("FLIGHT")) {
|
||||
//cerr << "Found flight " << buffString << " size is : " << tokens.size() << endl;
|
||||
if (tokens.size() != 10) {
|
||||
SG_LOG(SG_AI, SG_ALERT, "Error parsing traffic file " << infileName << " at " << buffString);
|
||||
exit(1);
|
||||
}
|
||||
string callsign = tokens[1];
|
||||
string fltrules = tokens[2];
|
||||
string weekdays = tokens[3];
|
||||
string departurePort = tokens[5];
|
||||
string arrivalPort = tokens[7];
|
||||
int cruiseAlt = atoi(tokens[8].c_str());
|
||||
string depTimeGen = tokens[4];
|
||||
string arrTimeGen = tokens[6];
|
||||
string repeat = "WEEK";
|
||||
string requiredAircraft = tokens[9];
|
||||
|
||||
if (weekdays.size() != 7) {
|
||||
SG_LOG(SG_AI, SG_ALERT, "Found misconfigured weekdays string" << weekdays);
|
||||
exit(1);
|
||||
}
|
||||
depTime.clear();
|
||||
arrTime.clear();
|
||||
Tokenize(depTimeGen, depTime, ":");
|
||||
Tokenize(arrTimeGen, arrTime, ":");
|
||||
double dep = atof(depTime[0].c_str()) + (atof(depTime[1].c_str()) / 60.0);
|
||||
double arr = atof(arrTime[0].c_str()) + (atof(arrTime[1].c_str()) / 60.0);
|
||||
//cerr << "Using " << dep << " " << arr << endl;
|
||||
bool arrivalWeekdayNeedsIncrement = false;
|
||||
if (arr < dep) {
|
||||
arrivalWeekdayNeedsIncrement = true;
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
int j = i+1;
|
||||
if (weekdays[i] != '.') {
|
||||
char buffer[4];
|
||||
snprintf(buffer, 4, "%d/", j);
|
||||
string departureTime = string(buffer) + depTimeGen + string(":00");
|
||||
string arrivalTime;
|
||||
if (!arrivalWeekdayNeedsIncrement) {
|
||||
arrivalTime = string(buffer) + arrTimeGen + string(":00");
|
||||
}
|
||||
if (arrivalWeekdayNeedsIncrement && i != 6 ) {
|
||||
snprintf(buffer, 4, "%d/", j+1);
|
||||
arrivalTime = string(buffer) + arrTimeGen + string(":00");
|
||||
}
|
||||
if (arrivalWeekdayNeedsIncrement && i == 6 ) {
|
||||
snprintf(buffer, 4, "%d/", 0);
|
||||
arrivalTime = string(buffer) + arrTimeGen + string(":00");
|
||||
}
|
||||
SG_LOG(SG_AI, SG_ALERT, "Adding 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//exit(1);
|
||||
}
|
||||
|
||||
|
||||
void FGTrafficManager::Tokenize(const string& str,
|
||||
vector<string>& tokens,
|
||||
const string& delimiters)
|
||||
{
|
||||
// Skip delimiters at beginning.
|
||||
string::size_type lastPos = str.find_first_not_of(delimiters, 0);
|
||||
// Find first "non-delimiter".
|
||||
string::size_type pos = str.find_first_of(delimiters, lastPos);
|
||||
|
||||
while (string::npos != pos || string::npos != lastPos)
|
||||
{
|
||||
// Found a token, add it to the vector.
|
||||
tokens.push_back(str.substr(lastPos, pos - lastPos));
|
||||
// Skip delimiters. Note the "not_of"
|
||||
lastPos = str.find_first_not_of(delimiters, pos);
|
||||
// Find next "non-delimiter"
|
||||
pos = str.find_first_of(delimiters, lastPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<FGTrafficManager> registrantFGTrafficManager(
|
||||
SGSubsystemMgr::POST_FDM,
|
||||
{{"terrasync", SGSubsystemMgr::Dependency::HARD},
|
||||
{"PerformanceDB", SGSubsystemMgr::Dependency::HARD}});
|
||||
128
src/Traffic/TrafficMgr.hxx
Normal file
128
src/Traffic/TrafficMgr.hxx
Normal file
@@ -0,0 +1,128 @@
|
||||
/* -*- Mode: C++ -*- *****************************************************
|
||||
* TrafficMgr.hxx
|
||||
* Written by Durk Talsma. Started May 5, 2004
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************
|
||||
* This file contains the class definitions for a (Top Level) traffic
|
||||
* manager for FlightGear.
|
||||
*
|
||||
* This is traffic manager version II. The major difference from version
|
||||
* I is that the Flight Schedules are decoupled from the AIAircraft
|
||||
* entities. This allows for a much greater flexibility in setting up
|
||||
* Irregular schedules. Traffic Manager II also makes no longer use of .xml
|
||||
* based configuration files.
|
||||
*
|
||||
* Here is a step plan to achieve the goal of creating Traffic Manager II
|
||||
*
|
||||
* 1) Read aircraft data from a simple text file, like the one provided by
|
||||
* Gabor Toth
|
||||
* 2) Create a new database structure of SchedFlights. This new database
|
||||
* should not be part of the Schedule class, but of TrafficManager itself
|
||||
* 3) Each aircraft should have a list of possible Flights it can operate
|
||||
* (i.e. airline and AC type match).
|
||||
* 4) Aircraft processing proceeds as current. During initialization, we seek
|
||||
* the most urgent flight that needs to be operated
|
||||
* 5) Modify the getNextLeg function so that the next flight is loaded smoothly.
|
||||
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _TRAFFICMGR_HXX_
|
||||
#define _TRAFFICMGR_HXX_
|
||||
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/props/propertyObject.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include "SchedFlight.hxx"
|
||||
#include "Schedule.hxx"
|
||||
|
||||
class Heuristic
|
||||
{
|
||||
public:
|
||||
std::string registration;
|
||||
unsigned int runCount;
|
||||
unsigned int hits;
|
||||
unsigned int lastRun;
|
||||
};
|
||||
|
||||
typedef std::vector<Heuristic> heuristicsVector;
|
||||
typedef std::vector<Heuristic>::iterator heuristicsVectorIterator;
|
||||
|
||||
typedef std::map < std::string, Heuristic> HeuristicMap;
|
||||
typedef HeuristicMap::iterator HeuristicMapIterator;
|
||||
|
||||
|
||||
class ScheduleParseThread;
|
||||
|
||||
class FGTrafficManager : public SGSubsystem
|
||||
{
|
||||
private:
|
||||
bool inited;
|
||||
bool doingInit;
|
||||
bool trafficSyncRequested;
|
||||
|
||||
double waitingMetarTime;
|
||||
std::string waitingMetarStation;
|
||||
|
||||
ScheduleVector scheduledAircraft;
|
||||
ScheduleVectorIterator currAircraft, currAircraftClosest;
|
||||
|
||||
FGScheduledFlightMap flights;
|
||||
|
||||
void readTimeTableFromFile(SGPath infilename);
|
||||
void Tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters = " ");
|
||||
|
||||
simgear::PropertyObject<bool> enabled, aiEnabled, realWxEnabled, metarValid, active, aiDataUpdateNow;
|
||||
|
||||
void loadHeuristics();
|
||||
|
||||
bool doDataSync();
|
||||
void finishInit();
|
||||
void shutdown();
|
||||
|
||||
friend class ScheduleParseThread;
|
||||
std::unique_ptr<ScheduleParseThread> scheduleParser;
|
||||
|
||||
// helper to read and parse the schedule data.
|
||||
// this is run on a helper thread, so be careful about
|
||||
// accessing properties during parsing
|
||||
void parseSchedule(const SGPath& path);
|
||||
|
||||
bool metarReady(double dt);
|
||||
|
||||
public:
|
||||
FGTrafficManager();
|
||||
~FGTrafficManager();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void update(double time) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "traffic-manager"; }
|
||||
|
||||
FGScheduledFlightVecIterator getFirstFlight(const std::string &ref) { return flights[ref].begin(); }
|
||||
FGScheduledFlightVecIterator getLastFlight(const std::string &ref) { return flights[ref].end(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user