]> git.mxchange.org Git - flightgear.git/blob - src/Environment/realwx_ctrl.cxx
Move viewer-related sources to separate folder.
[flightgear.git] / src / Environment / realwx_ctrl.cxx
1 // realwx_ctrl.cxx -- Process real weather data
2 //
3 // Written by David Megginson, started February 2002.
4 // Rewritten by Torsten Dreyer, August 2010, August 2011
5 //
6 // Copyright (C) 2002  David Megginson - david@megginson.com
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include "realwx_ctrl.hxx"
28
29 #include <algorithm>
30 #include <boost/foreach.hpp>
31
32 #include <simgear/structure/exception.hxx>
33 #include <simgear/misc/strutils.hxx>
34 #include <simgear/props/tiedpropertylist.hxx>
35 #include <simgear/io/HTTPRequest.hxx>
36 #include <simgear/timing/sg_time.hxx>
37 #include <simgear/structure/event_mgr.hxx>
38
39 #include "metarproperties.hxx"
40 #include "metarairportfilter.hxx"
41 #include "fgmetar.hxx"
42 #include <Network/HTTPClient.hxx>
43 #include <Main/fg_props.hxx>
44
45 namespace Environment {
46
47
48 /* -------------------------------------------------------------------------------- */
49
50 class MetarDataHandler {
51 public:
52     virtual void handleMetarData( const std::string & data ) = 0;
53 };
54
55 class MetarRequester {
56 public:
57     virtual void requestMetar( MetarDataHandler * metarDataHandler, const std::string & id ) = 0;
58 };
59
60 /* -------------------------------------------------------------------------------- */
61
62 class LiveMetarProperties : public MetarProperties, MetarDataHandler {
63 public:
64     LiveMetarProperties( SGPropertyNode_ptr rootNode, MetarRequester * metarRequester );
65     virtual ~LiveMetarProperties();
66     virtual void update( double dt );
67
68     virtual double getTimeToLive() const { return _timeToLive; }
69     virtual void setTimeToLive( double value ) { _timeToLive = value; }
70
71     // implementation of MetarDataHandler
72     virtual void handleMetarData( const std::string & data );
73
74     static const unsigned MAX_POLLING_INTERVAL_SECONDS = 10;
75     static const unsigned DEFAULT_TIME_TO_LIVE_SECONDS = 900;
76
77 private:
78     double _timeToLive;
79     double _pollingTimer;
80     MetarRequester * _metarRequester;
81 };
82
83 typedef SGSharedPtr<LiveMetarProperties> LiveMetarProperties_ptr;
84
85 LiveMetarProperties::LiveMetarProperties( SGPropertyNode_ptr rootNode, MetarRequester * metarRequester ) :
86     MetarProperties( rootNode ),
87     _timeToLive(0.0),
88     _pollingTimer(0.0),
89     _metarRequester(metarRequester)
90 {
91     _tiedProperties.Tie("time-to-live", &_timeToLive );
92 }
93
94 LiveMetarProperties::~LiveMetarProperties()
95 {
96     _tiedProperties.Untie();
97 }
98
99 void LiveMetarProperties::update( double dt )
100 {
101     _timeToLive -= dt;
102     _pollingTimer -= dt;
103     if( _timeToLive < 0.0 ) {
104         _timeToLive = 0.0;
105         std::string stationId = getStationId();
106         if( stationId.empty() ) return;
107         if( _pollingTimer > 0.0 ) return;
108         _metarRequester->requestMetar( this, stationId );
109         _pollingTimer = MAX_POLLING_INTERVAL_SECONDS;
110     }
111 }
112
113 void LiveMetarProperties::handleMetarData( const std::string & data )
114 {
115     SG_LOG( SG_ENVIRONMENT, SG_INFO, "LiveMetarProperties::handleMetarData() received METAR for " << getStationId() << ": " << data );
116     _timeToLive = DEFAULT_TIME_TO_LIVE_SECONDS;
117     setMetar( data );
118 }
119
120 /* -------------------------------------------------------------------------------- */
121
122 class BasicRealWxController : public RealWxController
123 {
124 public:
125     BasicRealWxController( SGPropertyNode_ptr rootNode, MetarRequester * metarRequester );
126     virtual ~BasicRealWxController ();
127
128     virtual void init ();
129     virtual void reinit ();
130     virtual void shutdown ();
131     
132 protected:
133     void bind();
134     void unbind();
135     void update( double dt );
136
137     void checkNearbyMetar();
138
139     long getMetarMaxAgeMin() const { return _max_age_n == NULL ? 0 : _max_age_n->getLongValue(); }
140
141     SGPropertyNode_ptr _rootNode;
142     SGPropertyNode_ptr _ground_elevation_n;
143     SGPropertyNode_ptr _max_age_n;
144
145     bool _enabled;
146     bool __enabled;
147     simgear::TiedPropertyList _tiedProperties;
148     typedef std::vector<LiveMetarProperties_ptr> MetarPropertiesList;
149     MetarPropertiesList _metarProperties;
150
151 };
152
153 /* -------------------------------------------------------------------------------- */
154 /*
155 Properties
156  ~/enabled: bool              Enables/Disables the realwx controller
157  ~/metar[1..n]: string        Target property path for metar data
158  */
159
160 BasicRealWxController::BasicRealWxController( SGPropertyNode_ptr rootNode, MetarRequester * metarRequester ) :
161   _rootNode(rootNode),
162   _ground_elevation_n( fgGetNode( "/position/ground-elev-m", true )),
163   _max_age_n( fgGetNode( "/environment/params/metar-max-age-min", false ) ),
164   _enabled(true),
165   __enabled(false)
166 {
167     // at least instantiate MetarProperties for /environment/metar
168     _metarProperties.push_back( new LiveMetarProperties( 
169             fgGetNode( rootNode->getStringValue("metar", "/environment/metar"), true ), metarRequester ));
170
171     BOOST_FOREACH( SGPropertyNode_ptr n, rootNode->getChildren("metar") ) {
172        SGPropertyNode_ptr metarNode = fgGetNode( n->getStringValue(), true );
173
174        // check for duplicate entries
175        bool existingElement = false;
176        BOOST_FOREACH( LiveMetarProperties_ptr p, _metarProperties ) {
177          if( p->get_root_node()->getPath() == metarNode->getPath() ) {
178            existingElement = true;
179            break;
180          }
181        }
182
183        if( existingElement )
184          continue;
185
186        SG_LOG( SG_ENVIRONMENT, SG_INFO, "Adding metar properties at " << metarNode->getPath() );
187         _metarProperties.push_back( new LiveMetarProperties( metarNode, metarRequester ));
188     }
189 }
190
191 BasicRealWxController::~BasicRealWxController()
192 {
193 }
194
195 void BasicRealWxController::init()
196 {
197     __enabled = false;
198     update(0); // fetch data ASAP
199     
200     globals->get_event_mgr()->addTask("checkNearbyMetar", this,
201                                       &BasicRealWxController::checkNearbyMetar, 60 );
202 }
203
204 void BasicRealWxController::reinit()
205 {
206     __enabled = false;
207 }
208     
209 void BasicRealWxController::shutdown()
210 {
211     globals->get_event_mgr()->removeTask("checkNearbyMetar");
212 }
213
214 void BasicRealWxController::bind()
215 {
216     _tiedProperties.setRoot( _rootNode );
217     _tiedProperties.Tie( "enabled", &_enabled );
218 }
219
220 void BasicRealWxController::unbind()
221 {
222     _tiedProperties.Untie();
223 }
224
225 void BasicRealWxController::update( double dt )
226 {
227   if( _enabled ) {
228     bool firstIteration = !__enabled; // first iteration after being enabled?
229
230     // clock tick for every METAR in stock
231     BOOST_FOREACH(LiveMetarProperties* p, _metarProperties) {
232       // first round? All received METARs are outdated
233       if( firstIteration ) p->setTimeToLive( 0.0 );
234       p->update(dt);
235     }
236
237     __enabled = true;
238   } else {
239     __enabled = false;
240   }
241 }
242
243 void BasicRealWxController::checkNearbyMetar()
244 {
245     try {
246       const SGGeod & pos = globals->get_aircraft_position();
247
248       // check nearest airport
249       SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "NoaaMetarRealWxController::update(): (re) checking nearby airport with METAR" );
250
251       FGAirport * nearestAirport = FGAirport::findClosest(pos, 10000.0, MetarAirportFilter::instance() );
252       if( nearestAirport == NULL ) {
253           SG_LOG(SG_ENVIRONMENT,SG_WARN,"RealWxController::update can't find airport with METAR within 10000NM"  );
254           return;
255       }
256
257       SG_LOG(SG_ENVIRONMENT, SG_DEBUG, 
258           "NoaaMetarRealWxController::update(): nearest airport with METAR is: " << nearestAirport->ident() );
259
260       // if it has changed, invalidate the associated METAR
261       if( _metarProperties[0]->getStationId() != nearestAirport->ident() ) {
262           SG_LOG(SG_ENVIRONMENT, SG_INFO, 
263               "NoaaMetarRealWxController::update(): nearest airport with METAR has changed. Old: '" << 
264               _metarProperties[0]->getStationId() <<
265               "', new: '" << nearestAirport->ident() << "'" );
266           _metarProperties[0]->setStationId( nearestAirport->ident() );
267           _metarProperties[0]->setTimeToLive( 0.0 );
268       }
269     }
270     catch( sg_exception & ) {
271       return;
272     }
273     
274 }
275
276 /* -------------------------------------------------------------------------------- */
277
278 class NoaaMetarRealWxController : public BasicRealWxController, MetarRequester {
279 public:
280     NoaaMetarRealWxController( SGPropertyNode_ptr rootNode );
281
282     // implementation of MetarRequester
283     virtual void requestMetar( MetarDataHandler * metarDataHandler, const std::string & id );
284
285 private:
286     
287 };
288
289 NoaaMetarRealWxController::NoaaMetarRealWxController( SGPropertyNode_ptr rootNode ) :
290   BasicRealWxController(rootNode, this )
291 {
292 }
293
294 void NoaaMetarRealWxController::requestMetar( MetarDataHandler * metarDataHandler, const std::string & id )
295 {
296     class NoaaMetarGetRequest : public simgear::HTTP::Request
297     {
298     public:
299         NoaaMetarGetRequest(MetarDataHandler* metarDataHandler, const string& stationId ) :
300               Request("http://weather.noaa.gov/pub/data/observations/metar/stations/" + stationId + ".TXT"),
301               _fromProxy(false),
302               _metarDataHandler(metarDataHandler)
303           {
304           }
305
306           virtual string_list requestHeaders() const
307           {
308               string_list reply;
309               reply.push_back("X-TIME");
310               return reply;
311           }
312
313           virtual std::string header(const std::string& name) const
314           {
315               string reply;
316
317               if( name == "X-TIME" ) {
318                   std::ostringstream buf;
319                   buf <<  globals->get_time_params()->get_cur_time();
320                   reply = buf.str();
321               }
322
323               return reply;
324           }
325
326           virtual void responseHeader(const string& key, const string& value)
327           {
328               if (key == "x-metarproxy") {
329                   _fromProxy = true;
330               }
331           }
332
333           virtual void gotBodyData(const char* s, int n)
334           {
335               _metar += string(s, n);
336           }
337
338           virtual void responseComplete()
339           {
340               if (responseCode() == 200) {
341                   _metarDataHandler->handleMetarData( simgear::strutils::simplify(_metar) );
342               } else {
343                   SG_LOG(SG_ENVIRONMENT, SG_WARN, "metar download failed:" << url() << ": reason:" << responseReason());
344               }
345           }
346
347 //          bool fromMetarProxy() const
348 //          { return _fromProxy; }
349     private:  
350         string _metar;
351         bool _fromProxy;
352         MetarDataHandler * _metarDataHandler;
353     };
354
355
356     SG_LOG(SG_ENVIRONMENT, SG_INFO, 
357         "NoaaMetarRealWxController::update(): spawning load request for station-id '" << id << "'" );
358     FGHTTPClient::instance()->makeRequest(new NoaaMetarGetRequest(metarDataHandler, id));
359 }
360
361 /* -------------------------------------------------------------------------------- */
362
363 RealWxController * RealWxController::createInstance( SGPropertyNode_ptr rootNode )
364 {
365 //    string dataSource = rootNode->getStringValue("data-source", "noaa" );
366 //    if( dataSource == "nwx" ) {
367 //      return new NwxMetarRealWxController( rootNode );
368 //    } else {
369       return new NoaaMetarRealWxController( rootNode );
370 //    }
371 }
372
373 /* -------------------------------------------------------------------------------- */
374
375 } // namespace Environment