#include <simgear/structure/exception.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/props/tiedpropertylist.hxx>
-#include <simgear/io/HTTPRequest.hxx>
+#include <simgear/io/HTTPMemoryRequest.hxx>
#include <simgear/timing/sg_time.hxx>
#include <simgear/structure/event_mgr.hxx>
#include <simgear/structure/commands.hxx>
/* -------------------------------------------------------------------------------- */
-class MetarDataHandler {
-public:
- virtual void handleMetarData( const std::string & data ) = 0;
-};
-
-class MetarRequester {
-public:
- virtual void requestMetar( MetarDataHandler * metarDataHandler, const std::string & id ) = 0;
-};
+class MetarRequester;
/* -------------------------------------------------------------------------------- */
-class LiveMetarProperties : public MetarProperties, MetarDataHandler {
+class LiveMetarProperties : public MetarProperties {
public:
- LiveMetarProperties( SGPropertyNode_ptr rootNode, MetarRequester * metarRequester );
+ LiveMetarProperties( SGPropertyNode_ptr rootNode, MetarRequester * metarRequester, int maxAge );
virtual ~LiveMetarProperties();
virtual void update( double dt );
virtual double getTimeToLive() const { return _timeToLive; }
- virtual void setTimeToLive( double value ) { _timeToLive = value; }
+ virtual void resetTimeToLive()
+ { _timeToLive = 0.00; _pollingTimer = 0.0; }
// implementation of MetarDataHandler
virtual void handleMetarData( const std::string & data );
-
+ virtual void handleMetarFailure();
+
static const unsigned MAX_POLLING_INTERVAL_SECONDS = 10;
static const unsigned DEFAULT_TIME_TO_LIVE_SECONDS = 900;
double _timeToLive;
double _pollingTimer;
MetarRequester * _metarRequester;
+ int _maxAge;
+ bool _failure;
};
typedef SGSharedPtr<LiveMetarProperties> LiveMetarProperties_ptr;
-LiveMetarProperties::LiveMetarProperties( SGPropertyNode_ptr rootNode, MetarRequester * metarRequester ) :
+class MetarRequester {
+public:
+ virtual void requestMetar( LiveMetarProperties_ptr metarDataHandler, const std::string & id ) = 0;
+};
+
+
+LiveMetarProperties::LiveMetarProperties( SGPropertyNode_ptr rootNode, MetarRequester * metarRequester, int maxAge ) :
MetarProperties( rootNode ),
_timeToLive(0.0),
_pollingTimer(0.0),
- _metarRequester(metarRequester)
+ _metarRequester(metarRequester),
+ _maxAge(maxAge),
+ _failure(false)
{
_tiedProperties.Tie("time-to-live", &_timeToLive );
+ _tiedProperties.Tie("failure", &_failure);
}
LiveMetarProperties::~LiveMetarProperties()
{
_timeToLive -= dt;
_pollingTimer -= dt;
- if( _timeToLive < 0.0 ) {
+ if( _timeToLive <= 0.0 ) {
_timeToLive = 0.0;
+ invalidate();
std::string stationId = getStationId();
if( stationId.empty() ) return;
if( _pollingTimer > 0.0 ) return;
void LiveMetarProperties::handleMetarData( const std::string & data )
{
- SG_LOG( SG_ENVIRONMENT, SG_INFO, "LiveMetarProperties::handleMetarData() received METAR for " << getStationId() << ": " << data );
+ SG_LOG( SG_ENVIRONMENT, SG_DEBUG, "LiveMetarProperties::handleMetarData() received METAR for " << getStationId() << ": " << data );
_timeToLive = DEFAULT_TIME_TO_LIVE_SECONDS;
- setMetar( data );
+
+ SGSharedPtr<FGMetar> m;
+ try {
+ m = new FGMetar(data.c_str());
+ }
+ catch( sg_io_exception &) {
+ SG_LOG( SG_ENVIRONMENT, SG_WARN, "Can't parse metar: " << data );
+ _failure = true;
+ return;
+ }
+
+ if (_maxAge && (m->getAge_min() > _maxAge)) {
+ // METAR is older than max-age, ignore
+ SG_LOG( SG_ENVIRONMENT, SG_DEBUG, "Ignoring outdated METAR for " << getStationId());
+ return;
+ }
+
+ _failure = false;
+ setMetar( m );
}
+void LiveMetarProperties::handleMetarFailure()
+{
+ _failure = true;
+}
+
/* -------------------------------------------------------------------------------- */
class BasicRealWxController : public RealWxController
void addMetarAtPath(const string& propPath, const string& icao);
void removeMetarAtPath(const string& propPath);
+
+ typedef std::vector<LiveMetarProperties_ptr> MetarPropertiesList;
+ MetarPropertiesList::iterator findMetarAtPath(const string &propPath);
protected:
void bind();
void unbind();
bool _enabled;
bool _wasEnabled;
simgear::TiedPropertyList _tiedProperties;
- typedef std::vector<LiveMetarProperties_ptr> MetarPropertiesList;
MetarPropertiesList _metarProperties;
MetarRequester* _requester;
_wasEnabled(false),
_requester(metarRequester)
{
- // at least instantiate MetarProperties for /environment/metar
- _metarProperties.push_back( new LiveMetarProperties(
- fgGetNode( rootNode->getStringValue("metar", "/environment/metar"), true ), metarRequester ));
-
- BOOST_FOREACH( SGPropertyNode_ptr n, rootNode->getChildren("metar") ) {
- SGPropertyNode_ptr metarNode = fgGetNode( n->getStringValue(), true );
- addMetarAtPath(metarNode->getPath(), "");
- }
-
- SGCommandMgr::instance()->addCommand("request-metar", commandRequestMetar);
- SGCommandMgr::instance()->addCommand("clear-metar", commandClearMetar);
+
+ globals->get_commands()->addCommand("request-metar", commandRequestMetar);
+ globals->get_commands()->addCommand("clear-metar", commandClearMetar);
}
BasicRealWxController::~BasicRealWxController()
{
- //SGCommandMgr::instance()->removeCommand("request-metar");
+ globals->get_commands()->removeCommand("request-metar");
+ globals->get_commands()->removeCommand("clear-metar");
}
void BasicRealWxController::init()
{
_wasEnabled = false;
+
+ // at least instantiate MetarProperties for /environment/metar
+ SGPropertyNode_ptr metarNode = fgGetNode( _rootNode->getStringValue("metar", "/environment/metar"), true );
+ _metarProperties.push_back( new LiveMetarProperties(metarNode,
+ _requester,
+ getMetarMaxAgeMin()));
+
+ BOOST_FOREACH( SGPropertyNode_ptr n, _rootNode->getChildren("metar") ) {
+ SGPropertyNode_ptr metarNode = fgGetNode( n->getStringValue(), true );
+ addMetarAtPath(metarNode->getPath(), "");
+ }
+
+ checkNearbyMetar();
update(0); // fetch data ASAP
globals->get_event_mgr()->addTask("checkNearbyMetar", this,
void BasicRealWxController::reinit()
{
_wasEnabled = false;
+ checkNearbyMetar();
+ update(0); // fetch data ASAP
}
void BasicRealWxController::shutdown()
}
void BasicRealWxController::update( double dt )
-{
+{
if( _enabled ) {
bool firstIteration = !_wasEnabled;
-
// clock tick for every METAR in stock
BOOST_FOREACH(LiveMetarProperties* p, _metarProperties) {
// first round? All received METARs are outdated
- if( firstIteration ) p->setTimeToLive( 0.0 );
+ if( firstIteration ) p->resetTimeToLive();
p->update(dt);
}
void BasicRealWxController::addMetarAtPath(const string& propPath, const string& icao)
{
// check for duplicate entries
- BOOST_FOREACH( LiveMetarProperties_ptr p, _metarProperties ) {
- if( p->get_root_node()->getPath() == propPath ) {
- // already exists
- if (p->getStationId() != icao) {
- p->setStationId(icao);
- p->setTimeToLive(0.0);
- }
-
- return;
+ MetarPropertiesList::iterator it = findMetarAtPath(propPath);
+ if( it != _metarProperties.end() ) {
+ SG_LOG( SG_ENVIRONMENT, SG_INFO, "Reusing metar properties at " << propPath << " for " << icao);
+ // already exists
+ if ((*it)->getStationId() != icao) {
+ (*it)->setStationId(icao);
+ (*it)->resetTimeToLive();
}
- } // of exitsing metar properties iteration
+ return;
+ }
SGPropertyNode_ptr metarNode = fgGetNode(propPath, true);
- SG_LOG( SG_ENVIRONMENT, SG_INFO, "Adding metar properties at " << propPath );
- LiveMetarProperties_ptr p(new LiveMetarProperties( metarNode, _requester ));
+ SG_LOG( SG_ENVIRONMENT, SG_INFO, "Adding metar properties at " << propPath << " for " << icao);
+ LiveMetarProperties_ptr p(new LiveMetarProperties( metarNode, _requester, getMetarMaxAgeMin() ));
_metarProperties.push_back(p);
p->setStationId(icao);
}
void BasicRealWxController::removeMetarAtPath(const string &propPath)
{
- MetarPropertiesList::iterator it = _metarProperties.begin();
- for (; it != _metarProperties.end(); ++it) {
- LiveMetarProperties_ptr p(*it);
- if( p->get_root_node()->getPath() == propPath ) {
- _metarProperties.erase(it);
- // final ref will drop, and delete the MetarProperties, when we return
- return;
- }
+ MetarPropertiesList::iterator it = findMetarAtPath( propPath );
+ if( it != _metarProperties.end() ) {
+ SG_LOG(SG_ENVIRONMENT, SG_INFO, "removing metar properties at " << propPath);
+ _metarProperties.erase(it);
+ } else {
+ SG_LOG(SG_ENVIRONMENT, SG_WARN, "no metar properties at " << propPath);
}
-
- SG_LOG(SG_ENVIRONMENT, SG_WARN, "no metar properties at " << propPath);
+}
+
+BasicRealWxController::MetarPropertiesList::iterator BasicRealWxController::findMetarAtPath(const string &propPath)
+{
+ // don not compare unprocessed property path
+ // /foo/bar[0]/baz equals /foo/bar/baz
+ SGPropertyNode_ptr n = fgGetNode(propPath,false);
+ if( false == n.valid() ) // trivial: node does not exist
+ return _metarProperties.end();
+
+ MetarPropertiesList::iterator it = _metarProperties.begin();
+ while( it != _metarProperties.end() &&
+ (*it)->get_root_node()->getPath() != n->getPath() )
+ ++it;
+
+ return it;
}
void BasicRealWxController::checkNearbyMetar()
_metarProperties[0]->getStationId() <<
"', new: '" << nearestAirport->ident() << "'" );
_metarProperties[0]->setStationId( nearestAirport->ident() );
- _metarProperties[0]->setTimeToLive( 0.0 );
+ _metarProperties[0]->resetTimeToLive();
}
}
catch( sg_exception & ) {
NoaaMetarRealWxController( SGPropertyNode_ptr rootNode );
// implementation of MetarRequester
- virtual void requestMetar( MetarDataHandler * metarDataHandler, const std::string & id );
+ virtual void requestMetar( LiveMetarProperties_ptr metarDataHandler, const std::string & id );
+ virtual ~NoaaMetarRealWxController()
+ {
+ }
private:
};
{
}
-void NoaaMetarRealWxController::requestMetar( MetarDataHandler * metarDataHandler, const std::string & id )
+void NoaaMetarRealWxController::requestMetar
+(
+ LiveMetarProperties_ptr metarDataHandler,
+ const std::string& id
+)
{
- class NoaaMetarGetRequest : public simgear::HTTP::Request
- {
+ static const std::string NOAA_BASE_URL =
+ "http://weather.noaa.gov/pub/data/observations/metar/stations/";
+ class NoaaMetarGetRequest:
+ public simgear::HTTP::MemoryRequest
+ {
public:
- NoaaMetarGetRequest(MetarDataHandler* metarDataHandler, const string& stationId ) :
- Request("http://weather.noaa.gov/pub/data/observations/metar/stations/" + stationId + ".TXT"),
- _fromProxy(false),
- _metarDataHandler(metarDataHandler)
- {
- }
-
- virtual string_list requestHeaders() const
- {
- string_list reply;
- reply.push_back("X-TIME");
- return reply;
- }
-
- virtual std::string header(const std::string& name) const
- {
- string reply;
-
- if( name == "X-TIME" ) {
- std::ostringstream buf;
- buf << globals->get_time_params()->get_cur_time();
- reply = buf.str();
- }
-
- return reply;
- }
-
- virtual void responseHeader(const string& key, const string& value)
- {
- if (key == "x-metarproxy") {
- _fromProxy = true;
- }
- }
-
- virtual void gotBodyData(const char* s, int n)
- {
- _metar += string(s, n);
- }
-
- virtual void responseComplete()
- {
- if (responseCode() == 200) {
- _metarDataHandler->handleMetarData( simgear::strutils::simplify(_metar) );
- } else {
- SG_LOG(SG_ENVIRONMENT, SG_WARN, "metar download failed:" << url() << ": reason:" << responseReason());
- }
- }
-
-// bool fromMetarProxy() const
-// { return _fromProxy; }
- private:
- string _metar;
- bool _fromProxy;
- MetarDataHandler * _metarDataHandler;
- };
-
- string upperId = boost::to_upper_copy(id);
-
- SG_LOG(SG_ENVIRONMENT, SG_INFO,
- "NoaaMetarRealWxController::update(): spawning load request for station-id '" << upperId << "'" );
- FGHTTPClient::instance()->makeRequest(new NoaaMetarGetRequest(metarDataHandler, upperId));
+ NoaaMetarGetRequest( LiveMetarProperties_ptr metarDataHandler,
+ const std::string& stationId ):
+ MemoryRequest(NOAA_BASE_URL + stationId + ".TXT"),
+ _metarDataHandler(metarDataHandler)
+ {
+ std::ostringstream buf;
+ buf << globals->get_time_params()->get_cur_time();
+ requestHeader("X-TIME") = buf.str();
+ }
+
+ virtual void onDone()
+ {
+ if( responseCode() != 200 )
+ {
+ SG_LOG
+ (
+ SG_ENVIRONMENT,
+ SG_WARN,
+ "metar download failed:" << url() << ": reason:" << responseReason()
+ );
+ return;
+ }
+
+ _metarDataHandler->handleMetarData
+ (
+ simgear::strutils::simplify(responseBody())
+ );
+ }
+
+ virtual void onFail()
+ {
+ SG_LOG(SG_ENVIRONMENT, SG_INFO, "metar download failure");
+ _metarDataHandler->handleMetarFailure();
+ }
+
+ private:
+ LiveMetarProperties_ptr _metarDataHandler;
+ };
+
+ string upperId = boost::to_upper_copy(id);
+
+ SG_LOG
+ (
+ SG_ENVIRONMENT,
+ SG_INFO,
+ "NoaaMetarRealWxController::update(): "
+ "spawning load request for station-id '" << upperId << "'"
+ );
+ FGHTTPClient* http = static_cast<FGHTTPClient*>(globals->get_subsystem("http"));
+ if (http) {
+ http->makeRequest(new NoaaMetarGetRequest(metarDataHandler, upperId));
+ }
}
/* -------------------------------------------------------------------------------- */
-
+
RealWxController * RealWxController::createInstance( SGPropertyNode_ptr rootNode )
{
return new NoaaMetarRealWxController( rootNode );
}
+
+RealWxController::~RealWxController()
+{
+}
/* -------------------------------------------------------------------------------- */