18#include <simgear/structure/exception.hxx>
19#include <simgear/misc/strutils.hxx>
20#include <simgear/props/tiedpropertylist.hxx>
21#include <simgear/io/HTTPMemoryRequest.hxx>
22#include <simgear/timing/sg_time.hxx>
23#include <simgear/structure/event_mgr.hxx>
24#include <simgear/structure/commands.hxx>
46 virtual void update(
double dt );
50 { _timeToLive = 0.00; _pollingTimer = 0.0; }
79 _metarRequester(metarRequester),
96 if( _timeToLive <= 0.0 ) {
100 if( stationId.empty() )
return;
101 if( _pollingTimer > 0.0 )
return;
102 _metarRequester->requestMetar(
this, stationId );
109 SG_LOG( SG_ENVIRONMENT, SG_DEBUG,
"LiveMetarProperties::handleMetarData() received METAR for " <<
getStationId() <<
": " << data );
112 SGSharedPtr<FGMetar> m;
113 static bool haveReportedMETARFailure =
false;
117 catch( sg_io_exception &e) {
118 SG_LOG( SG_ENVIRONMENT, SG_WARN,
"Can't parse metar: " << data <<
119 " (" << e.getFormattedMessage() <<
")");
122 if (!haveReportedMETARFailure) {
123 haveReportedMETARFailure =
true;
130 if (_maxAge && (m->getAge_min() > _maxAge)) {
132 SG_LOG( SG_ENVIRONMENT, SG_ALERT,
"Ignoring outdated METAR for " <<
getStationId() <<
" (see /environment/params/metar-max-age-min)");
154 void bind()
override;
155 void init()
override;
159 void update(
double dt)
override;
167 void addMetarAtPath(
const std::string& propPath,
const std::string& icao);
172 MetarPropertiesList::iterator
findMetarAtPath(
const std::string &propPath);
192 auto envMgr = (SGSubsystemGroup*)
globals->get_subsystem_mgr()->get_subsystem(
"environment");
202 std::string icao(arg->getStringValue(
"station"));
203 std::transform(icao.begin(), icao.end(), icao.begin(),
static_cast<int(*)(
int)
>(std::toupper));
205 std::string path = arg->getStringValue(
"path");
212 auto envMgr = (SGSubsystemGroup*)
globals->get_subsystem_mgr()->get_subsystem(
"environment");
222 std::string path = arg->getStringValue(
"path");
249 globals->get_commands()->removeCommand(
"request-metar");
250 globals->get_commands()->removeCommand(
"clear-metar");
258 SGPropertyNode_ptr metarNode =
fgGetNode(
_rootNode->getStringValue(
"metar",
"/environment/metar"),
true );
263 for(
auto n :
_rootNode->getChildren(
"metar") ) {
264 SGPropertyNode_ptr metarNode =
fgGetNode( n->getStringValue(),
true );
271 globals->get_event_mgr()->addTask(
"checkNearbyMetar",
284 globals->get_event_mgr()->removeTask(
"checkNearbyMetar");
305 if( firstIteration )
p->resetTimeToLive();
320 SG_LOG( SG_ENVIRONMENT, SG_INFO,
"Reusing metar properties at " << propPath <<
" for " << icao);
322 if ((*it)->getStationId() != icao) {
323 (*it)->setStationId(icao);
324 (*it)->resetTimeToLive();
329 SGPropertyNode_ptr metarNode =
fgGetNode(propPath,
true);
330 SG_LOG( SG_ENVIRONMENT, SG_INFO,
"Adding metar properties at " << propPath <<
" for " << icao);
333 p->setStationId(icao);
340 SG_LOG(SG_ENVIRONMENT, SG_INFO,
"removing metar properties at " << propPath);
343 SG_LOG(SG_ENVIRONMENT, SG_WARN,
"no metar properties at " << propPath);
351 SGPropertyNode_ptr n =
fgGetNode(propPath,
false);
357 (*it)->get_root_node()->getPath() != n->getPath() )
366 const SGGeod & pos =
globals->get_aircraft_position();
369 SG_LOG(SG_ENVIRONMENT, SG_DEBUG,
"NoaaMetarRealWxController::update(): (re) checking nearby airport with METAR" );
372 if( nearestAirport == NULL ) {
373 SG_LOG(SG_ENVIRONMENT,SG_WARN,
"RealWxController::update can't find airport with METAR within 10000NM" );
377 SG_LOG(SG_ENVIRONMENT, SG_DEBUG,
378 "NoaaMetarRealWxController::update(): nearest airport with METAR is: " << nearestAirport->
ident() );
382 SG_LOG(SG_ENVIRONMENT, SG_INFO,
383 "NoaaMetarRealWxController::update(): nearest airport with METAR has changed. Old: '" <<
385 "', new: '" << nearestAirport->
ident() <<
"'" );
390 catch( sg_exception & ) {
414 std::string noaa_base_url;
421 noaa_base_url =
"https://tgftp.nws.noaa.gov/data/observations/metar/stations/[station].TXT";
424 SGPropertyNode *urlNode =
_rootNode->getNode(
"metar-url",
false);
425 if (urlNode !=
nullptr)
426 noaa_base_url = urlNode->getStringValue();
432 const std::string&
id
435 class NoaaMetarGetRequest:
436 public simgear::HTTP::MemoryRequest
440 const std::string& stationId,
441 const std::string &base_url):
442 MemoryRequest( simgear::strutils::replace(base_url,
"[station]",stationId) ),
443 _metarDataHandler(metarDataHandler)
445 std::ostringstream buf;
446 buf <<
globals->get_time_params()->get_cur_time();
447 requestHeader(
"X-TIME") = buf.str();
450 virtual void onDone()
452 if( responseCode() != 200 )
458 "metar download failed:" << url() <<
": reason:" << responseReason()
463 _metarDataHandler->handleMetarData
465 simgear::strutils::simplify(responseBody())
469 virtual void onFail()
471 SG_LOG(SG_ENVIRONMENT, SG_INFO,
"metar download failure");
472 _metarDataHandler->handleMetarFailure();
479 std::string upperId = id;
480 std::transform(upperId.begin(), upperId.end(), upperId.begin(),
static_cast<int(*)(
int)
>(std::toupper));
486 "NoaaMetarRealWxController::update(): "
487 "spawning load request for station-id '" << upperId <<
"'"
491 http->makeRequest(
new NoaaMetarGetRequest(metarDataHandler, upperId, noaa_base_url));
497SGSubsystemMgr::Registrant<NoaaMetarRealWxController> registrantNoaaMetarRealWxController(
498 SGSubsystemMgr::GENERAL,
499 {{
"environment", SGSubsystemMgr::Dependency::SOFT},
500 {
"FGHTTPClient", SGSubsystemMgr::Dependency::SOFT},
501 {
"realwx", SGSubsystemMgr::Dependency::SOFT}});
SGPropertyNode_ptr _max_age_n
void addMetarAtPath(const std::string &propPath, const std::string &icao)
Create a metar-property binding at the specified property path, and initiate a request for the specif...
virtual ~BasicRealWxController()
std::vector< LiveMetarProperties_ptr > MetarPropertiesList
MetarPropertiesList::iterator findMetarAtPath(const std::string &propPath)
void removeMetarAtPath(const std::string &propPath)
BasicRealWxController(SGPropertyNode_ptr rootNode, MetarRequester *metarRequester)
MetarRequester * _requester
long getMetarMaxAgeMin() const
MetarPropertiesList _metarProperties
SGPropertyNode_ptr _rootNode
SGPropertyNode_ptr _ground_elevation_n
simgear::TiedPropertyList _tiedProperties
void update(double dt) override
virtual ~RealWxController()
static RealWxController * createInstance(SGPropertyNode_ptr rootNode)
static FGAirportRef findClosest(const SGGeod &aPos, double aCuttofNm, Filter *filter=NULL)
Syntactic wrapper around FGPositioned::findClosest - find the closest match for filter,...
const std::string & ident() const
static bool commandRequestMetar(const SGPropertyNode *arg, SGPropertyNode *root)
static bool commandClearMetar(const SGPropertyNode *arg, SGPropertyNode *root)
SGSharedPtr< LiveMetarProperties > LiveMetarProperties_ptr
void sentryReportException(const std::string &, const std::string &)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.