Switch RealWx to use simgear::HTTP class. Also tweak metar-proxy to send a HTTP status response.
This commit is contained in:
parent
c28825d9de
commit
3c9f4ad73e
2 changed files with 102 additions and 197 deletions
|
@ -31,14 +31,16 @@
|
||||||
|
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
#include <simgear/misc/strutils.hxx>
|
#include <simgear/misc/strutils.hxx>
|
||||||
#include <simgear/props/tiedpropertylist.hxx>
|
#include <simgear/props/tiedpropertylist.hxx>
|
||||||
|
#include <simgear/io/HTTPClient.hxx>
|
||||||
|
#include <simgear/io/HTTPRequest.hxx>
|
||||||
|
#include <simgear/timing/sg_time.hxx>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#if defined(ENABLE_THREADS)
|
|
||||||
#include <OpenThreads/Thread>
|
|
||||||
#include <simgear/threads/SGQueue.hxx>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using simgear::PropertyList;
|
using simgear::PropertyList;
|
||||||
|
|
||||||
|
@ -110,6 +112,7 @@ protected:
|
||||||
simgear::TiedPropertyList _tiedProperties;
|
simgear::TiedPropertyList _tiedProperties;
|
||||||
typedef std::vector<LiveMetarProperties_ptr> MetarPropertiesList;
|
typedef std::vector<LiveMetarProperties_ptr> MetarPropertiesList;
|
||||||
MetarPropertiesList _metarProperties;
|
MetarPropertiesList _metarProperties;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------------- */
|
||||||
|
@ -195,86 +198,95 @@ public:
|
||||||
virtual void update (bool first, double delta_time_sec);
|
virtual void update (bool first, double delta_time_sec);
|
||||||
virtual void shutdown ();
|
virtual void shutdown ();
|
||||||
|
|
||||||
class MetarLoadRequest {
|
/**
|
||||||
public:
|
* callback from MetarGetRequest when a download succeeds
|
||||||
MetarLoadRequest( const string & stationId ) :
|
*/
|
||||||
_stationId(stationId),
|
void gotMetar(const string& stationId, const string& metar);
|
||||||
_proxyHost(fgGetNode("/sim/presets/proxy/host", true)->getStringValue()),
|
|
||||||
_proxyPort(fgGetNode("/sim/presets/proxy/port", true)->getStringValue()),
|
|
||||||
_proxyAuth(fgGetNode("/sim/presets/proxy/authentication", true)->getStringValue())
|
|
||||||
{}
|
|
||||||
MetarLoadRequest( const MetarLoadRequest & other ) :
|
|
||||||
_stationId(other._stationId),
|
|
||||||
_proxyHost(other._proxyAuth),
|
|
||||||
_proxyPort(other._proxyPort),
|
|
||||||
_proxyAuth(other._proxyAuth)
|
|
||||||
{}
|
|
||||||
string _stationId;
|
|
||||||
string _proxyHost;
|
|
||||||
string _proxyPort;
|
|
||||||
string _proxyAuth;
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
class MetarLoadResponse {
|
|
||||||
public:
|
|
||||||
MetarLoadResponse( const string & stationId, const string metar ) {
|
|
||||||
_stationId = stationId;
|
|
||||||
_metar = metar;
|
|
||||||
}
|
|
||||||
MetarLoadResponse( const MetarLoadResponse & other ) {
|
|
||||||
_stationId = other._stationId;
|
|
||||||
_metar = other._metar;
|
|
||||||
}
|
|
||||||
string _stationId;
|
|
||||||
string _metar;
|
|
||||||
};
|
|
||||||
private:
|
private:
|
||||||
double _positionTimeToLive;
|
double _positionTimeToLive;
|
||||||
double _requestTimer;
|
double _requestTimer;
|
||||||
|
|
||||||
#if defined(ENABLE_THREADS)
|
simgear::HTTP::Client _http;
|
||||||
class MetarLoadThread : public OpenThreads::Thread {
|
|
||||||
public:
|
|
||||||
MetarLoadThread( long maxAge );
|
|
||||||
virtual ~MetarLoadThread( ) { stop(); }
|
|
||||||
void requestMetar( const MetarLoadRequest & metarRequest, bool background = true );
|
|
||||||
bool hasMetar() { return _responseQueue.size() > 0; }
|
|
||||||
MetarLoadResponse getMetar() { return _responseQueue.pop(); }
|
|
||||||
virtual void run();
|
|
||||||
void stop();
|
|
||||||
private:
|
|
||||||
void fetch( const MetarLoadRequest & );
|
|
||||||
long _maxAge;
|
|
||||||
long _minRequestInterval;
|
|
||||||
volatile bool _stop;
|
|
||||||
SGBlockingQueue <MetarLoadRequest> _requestQueue;
|
|
||||||
SGBlockingQueue <MetarLoadResponse> _responseQueue;
|
|
||||||
};
|
|
||||||
|
|
||||||
MetarLoadThread * _metarLoadThread;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MetarGetRequest : public simgear::HTTP::Request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MetarGetRequest(NoaaMetarRealWxController* con, const string& s) :
|
||||||
|
Request(""),
|
||||||
|
stationId(s),
|
||||||
|
fromProxy(false),
|
||||||
|
wxController(con)
|
||||||
|
{
|
||||||
|
setUrl("http://weather.noaa.gov/pub/data/observations/metar/stations/" + stationId + ".TXT");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string_list requestHeaders() const
|
||||||
|
{
|
||||||
|
string_list r;
|
||||||
|
r.push_back("X-Time");
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string header(const string& name) const
|
||||||
|
{
|
||||||
|
if (name == "X-Time") {
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, 16, "%ld", globals->get_time_params()->get_cur_time());
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Request::header(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
wxController->gotMetar(stationId, metar);
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "metar download failed:" << url() << ": reason:" << responseReason());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fromMetarProxy() const
|
||||||
|
{ return fromProxy; }
|
||||||
|
private:
|
||||||
|
string stationId;
|
||||||
|
string metar;
|
||||||
|
bool fromProxy;
|
||||||
|
NoaaMetarRealWxController* wxController;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NoaaMetarRealWxController::NoaaMetarRealWxController( SGPropertyNode_ptr rootNode ) :
|
NoaaMetarRealWxController::NoaaMetarRealWxController( SGPropertyNode_ptr rootNode ) :
|
||||||
BasicRealWxController(rootNode),
|
BasicRealWxController(rootNode),
|
||||||
_positionTimeToLive(0.0),
|
_positionTimeToLive(0.0),
|
||||||
_requestTimer(0.0)
|
_requestTimer(0.0)
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_THREADS)
|
string proxyHost(fgGetString("/sim/presets/proxy/host"));
|
||||||
_metarLoadThread = new MetarLoadThread(getMetarMaxAgeMin());
|
int proxyPort(fgGetInt("/sim/presets/proxy/port"));
|
||||||
_metarLoadThread->start();
|
string proxyAuth(fgGetString("/sim/presets/proxy/auth"));
|
||||||
#endif
|
|
||||||
|
if (!proxyHost.empty()) {
|
||||||
|
_http.setProxy(proxyHost, proxyPort, proxyAuth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoaaMetarRealWxController::shutdown()
|
void NoaaMetarRealWxController::shutdown()
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_THREADS)
|
|
||||||
if( _metarLoadThread ) {
|
|
||||||
delete _metarLoadThread;
|
|
||||||
_metarLoadThread = NULL;
|
|
||||||
}
|
|
||||||
#endif // ENABLE_THREADS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NoaaMetarRealWxController::~NoaaMetarRealWxController()
|
NoaaMetarRealWxController::~NoaaMetarRealWxController()
|
||||||
|
@ -283,6 +295,8 @@ NoaaMetarRealWxController::~NoaaMetarRealWxController()
|
||||||
|
|
||||||
void NoaaMetarRealWxController::update( bool first, double dt )
|
void NoaaMetarRealWxController::update( bool first, double dt )
|
||||||
{
|
{
|
||||||
|
_http.update();
|
||||||
|
|
||||||
_positionTimeToLive -= dt;
|
_positionTimeToLive -= dt;
|
||||||
_requestTimer -= dt;
|
_requestTimer -= dt;
|
||||||
|
|
||||||
|
@ -315,143 +329,32 @@ void NoaaMetarRealWxController::update( bool first, double dt )
|
||||||
|
|
||||||
if( _requestTimer <= 0.0 ) {
|
if( _requestTimer <= 0.0 ) {
|
||||||
_requestTimer = 10.0;
|
_requestTimer = 10.0;
|
||||||
|
|
||||||
|
BOOST_FOREACH(LiveMetarProperties* p, _metarProperties) {
|
||||||
|
if( p->getTimeToLive() > 0.0 ) continue;
|
||||||
|
const std::string & stationId = p->getStationId();
|
||||||
|
if( stationId.empty() ) continue;
|
||||||
|
|
||||||
for( MetarPropertiesList::iterator it = _metarProperties.begin();
|
SG_LOG(SG_ALL, SG_INFO,
|
||||||
it != _metarProperties.end(); it++ ) {
|
"NoaaMetarRealWxController::update(): spawning load request for station-id '" << stationId << "'" );
|
||||||
|
|
||||||
if( (*it)->getTimeToLive() > 0.0 ) continue;
|
|
||||||
const std::string & stationId = (*it)->getStationId();
|
|
||||||
if( stationId.empty() ) continue;
|
|
||||||
|
|
||||||
SG_LOG(SG_ALL, SG_INFO,
|
|
||||||
"NoaaMetarRealWxController::update(): spawning load request for station-id '" << stationId << "'" );
|
|
||||||
|
|
||||||
MetarLoadRequest request( stationId );
|
_http.makeRequest(new MetarGetRequest(this, stationId));
|
||||||
// load the metar for the nearest airport in the foreground if the fdm is uninitialized
|
} // of MetarProperties iteration
|
||||||
// to make sure a metar is received
|
|
||||||
// before the automatic runway selection code runs. All subsequent calls
|
|
||||||
// run in the background
|
|
||||||
bool background = fgGetBool("/sim/fdm-initialized", false ) || it != _metarProperties.begin();
|
|
||||||
_metarLoadThread->requestMetar( request, background );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pick all the received responses from the result queue and update the associated
|
|
||||||
// property tree
|
|
||||||
while( _metarLoadThread->hasMetar() ) {
|
|
||||||
MetarLoadResponse metar = _metarLoadThread->getMetar();
|
|
||||||
SG_LOG( SG_ALL, SG_INFO, "NoaaMetarRwalWxController::update() received METAR for " << metar._stationId << ": " << metar._metar );
|
|
||||||
for( MetarPropertiesList::iterator it = _metarProperties.begin();
|
|
||||||
it != _metarProperties.end(); it++ ) {
|
|
||||||
if( (*it)->getStationId() != metar._stationId )
|
|
||||||
continue;
|
|
||||||
(*it)->setTimeToLive(900);
|
|
||||||
(*it)->setMetar( metar._metar );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------------- */
|
void NoaaMetarRealWxController::gotMetar(const string& stationId, const string& metar)
|
||||||
|
|
||||||
#if defined(ENABLE_THREADS)
|
|
||||||
NoaaMetarRealWxController::MetarLoadThread::MetarLoadThread( long maxAge ) :
|
|
||||||
_maxAge(maxAge),
|
|
||||||
_minRequestInterval(2000),
|
|
||||||
_stop(false)
|
|
||||||
{
|
{
|
||||||
}
|
SG_LOG( SG_ALL, SG_INFO, "NoaaMetarRwalWxController::update() received METAR for " << stationId << ": " << metar );
|
||||||
|
BOOST_FOREACH(LiveMetarProperties* p, _metarProperties) {
|
||||||
void NoaaMetarRealWxController::MetarLoadThread::requestMetar( const MetarLoadRequest & metarRequest, bool background )
|
if (p->getStationId() != stationId)
|
||||||
{
|
continue;
|
||||||
if( background ) {
|
|
||||||
if( _requestQueue.size() > 10 ) {
|
p->setTimeToLive(900);
|
||||||
SG_LOG(SG_ALL,SG_ALERT,
|
p->setMetar( metar );
|
||||||
"NoaaMetarRealWxController::MetarLoadThread::requestMetar() more than 10 outstanding METAR requests, dropping "
|
|
||||||
<< metarRequest._stationId );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_requestQueue.push( metarRequest );
|
|
||||||
} else {
|
|
||||||
fetch( metarRequest );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoaaMetarRealWxController::MetarLoadThread::stop()
|
|
||||||
{
|
|
||||||
// set stop flag and wake up the thread with an empty request
|
|
||||||
_stop = true;
|
|
||||||
MetarLoadRequest request("");
|
|
||||||
requestMetar(request);
|
|
||||||
join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NoaaMetarRealWxController::MetarLoadThread::run()
|
|
||||||
{
|
|
||||||
SGTimeStamp lastRun = SGTimeStamp::fromSec(0);
|
|
||||||
for( ;; ) {
|
|
||||||
SGTimeStamp dt = SGTimeStamp::now() - lastRun;
|
|
||||||
|
|
||||||
long delayMs = _minRequestInterval - dt.getSeconds() * 1000;
|
|
||||||
while (( delayMs > 0 ) && !_stop)
|
|
||||||
{
|
|
||||||
// sleep no more than 3 seconds at a time, otherwise shutdown response is too slow
|
|
||||||
long sleepMs = (delayMs>3000) ? 3000 : delayMs;
|
|
||||||
microSleep( sleepMs * 1000 );
|
|
||||||
delayMs -= sleepMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_stop)
|
|
||||||
break;
|
|
||||||
|
|
||||||
lastRun = SGTimeStamp::now();
|
|
||||||
|
|
||||||
const MetarLoadRequest request = _requestQueue.pop();
|
|
||||||
|
|
||||||
if (( request._stationId.size() == 0 ) || _stop)
|
|
||||||
break;
|
|
||||||
|
|
||||||
fetch( request );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NoaaMetarRealWxController::MetarLoadThread::fetch( const MetarLoadRequest & request )
|
|
||||||
{
|
|
||||||
SGSharedPtr<FGMetar> result = NULL;
|
|
||||||
|
|
||||||
try {
|
|
||||||
result = new FGMetar( request._stationId, request._proxyHost, request._proxyPort, request._proxyAuth );
|
|
||||||
_minRequestInterval = 2000;
|
|
||||||
} catch (const sg_io_exception& e) {
|
|
||||||
SG_LOG( SG_GENERAL, SG_WARN, "NoaaMetarRealWxController::fetchMetar(): can't get METAR for "
|
|
||||||
<< request._stationId << ":" << e.getFormattedMessage().c_str() );
|
|
||||||
_minRequestInterval += _minRequestInterval/2;
|
|
||||||
if( _minRequestInterval > 30000 )
|
|
||||||
_minRequestInterval = 30000;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string reply = result->getData();
|
|
||||||
std::replace(reply.begin(), reply.end(), '\n', ' ');
|
|
||||||
string metar = simgear::strutils::strip( reply );
|
|
||||||
|
|
||||||
if( metar.empty() ) {
|
|
||||||
SG_LOG( SG_GENERAL, SG_WARN, "NoaaMetarRealWxController::fetchMetar(): dropping empty METAR for "
|
|
||||||
<< request._stationId );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( _maxAge && result->getAge_min() > _maxAge ) {
|
|
||||||
SG_LOG( SG_GENERAL, SG_ALERT, "NoaaMetarRealWxController::fetchMetar(): dropping outdated METAR "
|
|
||||||
<< metar );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetarLoadResponse response( request._stationId, metar );
|
|
||||||
_responseQueue.push( response );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
RealWxController * RealWxController::createInstance( SGPropertyNode_ptr rootNode )
|
RealWxController * RealWxController::createInstance( SGPropertyNode_ptr rootNode )
|
||||||
|
|
|
@ -441,6 +441,8 @@ sub serve()
|
||||||
$metar .= sprintf "$icao %02d%02d%02dZ ", $day, $hour, $min;
|
$metar .= sprintf "$icao %02d%02d%02dZ ", $day, $hour, $min;
|
||||||
$metar .= $last_metar{$addr} || $METAR_DEFAULT;
|
$metar .= $last_metar{$addr} || $METAR_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print $client "HTTP/1.0 200 OK\015\012";
|
||||||
print $client "Content-Type: text/plain\015\012"
|
print $client "Content-Type: text/plain\015\012"
|
||||||
. "X-MetarProxy: nasse Maus\015\012"
|
. "X-MetarProxy: nasse Maus\015\012"
|
||||||
. "\015\012"
|
. "\015\012"
|
||||||
|
|
Loading…
Add table
Reference in a new issue