]> git.mxchange.org Git - flightgear.git/blob - src/Environment/realwx_ctrl.cxx
cmake changes for osg::CullSettings::ClearMask and Fedora
[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
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 #include "tiedpropertylist.hxx"
29 #include "metarproperties.hxx"
30 #include "metarairportfilter.hxx"
31 #include "fgmetar.hxx"
32
33 #include <Main/fg_props.hxx>
34
35 #include <simgear/structure/exception.hxx>
36 #include <simgear/misc/strutils.hxx>
37 #include <algorithm>
38 #if defined(ENABLE_THREADS)
39 #include <OpenThreads/Thread>
40 #include <simgear/threads/SGQueue.hxx>
41 #endif
42
43
44 namespace Environment {
45
46 class BasicRealWxController : public RealWxController
47 {
48 public:
49     BasicRealWxController( SGPropertyNode_ptr rootNode );
50     virtual ~BasicRealWxController ();
51
52     virtual void init ();
53     virtual void reinit ();
54
55 protected:
56     void bind();
57     void unbind();
58     void update( double dt );
59
60     virtual void update( bool first, double dt ) = 0;
61
62     long getMetarMaxAgeMin() const { return _max_age_n == NULL ? 0 : _max_age_n->getLongValue(); }
63
64     SGPropertyNode_ptr _rootNode;
65     SGPropertyNode_ptr _longitude_n;
66     SGPropertyNode_ptr _latitude_n;
67     SGPropertyNode_ptr _ground_elevation_n;
68     SGPropertyNode_ptr _max_age_n;
69
70     bool _enabled;
71     bool __enabled;
72     TiedPropertyList _tiedProperties;
73     MetarProperties  _metarProperties;
74 };
75
76 /* -------------------------------------------------------------------------------- */
77 /*
78 Properties
79  ~/enabled: bool              Enables/Disables the realwx controller
80  ~/metar[1..n]: string        Target property path for metar data
81  */
82
83 BasicRealWxController::BasicRealWxController( SGPropertyNode_ptr rootNode ) :
84   _rootNode(rootNode),
85   _longitude_n( fgGetNode( "/position/longitude-deg", true )),
86   _latitude_n( fgGetNode( "/position/latitude-deg", true )),
87   _ground_elevation_n( fgGetNode( "/position/ground-elev-m", true )),
88   _max_age_n( fgGetNode( "/environment/params/metar-max-age-min", false ) ),
89   _enabled(true),
90   __enabled(false),
91   _metarProperties( fgGetNode( rootNode->getStringValue("metar", "/environment/metar"), true ) )
92 {
93 }
94
95 BasicRealWxController::~BasicRealWxController()
96 {
97 }
98
99 void BasicRealWxController::init()
100 {
101     __enabled = false;
102     update(0); // fetch data ASAP
103 }
104
105 void BasicRealWxController::reinit()
106 {
107     __enabled = false;
108 }
109
110 void BasicRealWxController::bind()
111 {
112     _tiedProperties.setRoot( _rootNode );
113     _tiedProperties.Tie( "enabled", &_enabled );
114 }
115
116 void BasicRealWxController::unbind()
117 {
118     _tiedProperties.Untie();
119 }
120
121 void BasicRealWxController::update( double dt )
122 {
123   if( _enabled ) {
124     update( !__enabled, dt );
125     __enabled = true;
126   } else {
127     __enabled = false;
128   }
129 }
130
131 /* -------------------------------------------------------------------------------- */
132
133 class NoaaMetarRealWxController : public BasicRealWxController {
134 public:
135     NoaaMetarRealWxController( SGPropertyNode_ptr rootNode );
136     virtual ~NoaaMetarRealWxController();
137     virtual void update (bool first, double delta_time_sec);
138
139     class MetarLoadRequest {
140     public:
141         MetarLoadRequest( const string & stationId ) {
142             _stationId = stationId;
143             _proxyHost = fgGetNode("/sim/presets/proxy/host", true)->getStringValue();
144             _proxyPort = fgGetNode("/sim/presets/proxy/port", true)->getStringValue();
145             _proxyAuth = fgGetNode("/sim/presets/proxy/authentication", true)->getStringValue();
146         }
147         string _stationId;
148         string _proxyHost;
149         string _proxyPort;
150         string _proxyAuth;
151     private:
152     };
153 private:
154     double _metarTimeToLive;
155     double _positionTimeToLive;
156     double _minimumRequestInterval;
157     
158     SGPropertyNode_ptr _metarDataNode;
159     SGPropertyNode_ptr _metarValidNode;
160     SGPropertyNode_ptr _metarStationIdNode;
161
162
163 #if defined(ENABLE_THREADS)
164      class MetarLoadThread : public OpenThreads::Thread {
165      public:
166         MetarLoadThread( long maxAge );
167         void requestMetar( const MetarLoadRequest & metarRequest, bool background = true );
168         bool hasMetar() { return _responseQueue.size() > 0; }
169         string getMetar() { return _responseQueue.pop(); }
170         virtual void run();
171      private:
172         void fetch( const MetarLoadRequest & );
173         long _maxAge;
174         SGBlockingQueue <MetarLoadRequest> _requestQueue;
175         SGBlockingQueue <string> _responseQueue;
176      };
177
178      MetarLoadThread * _metarLoadThread;
179 #endif
180 };
181
182 NoaaMetarRealWxController::NoaaMetarRealWxController( SGPropertyNode_ptr rootNode ) :
183   BasicRealWxController(rootNode),
184   _metarTimeToLive(0.0),
185   _positionTimeToLive(0.0),
186   _minimumRequestInterval(0.0),
187   _metarDataNode(_metarProperties.get_root_node()->getNode("data",true)),
188   _metarValidNode(_metarProperties.get_root_node()->getNode("valid",true)),
189   _metarStationIdNode(_metarProperties.get_root_node()->getNode("station-id",true))
190 {
191 #if defined(ENABLE_THREADS)
192     _metarLoadThread = new MetarLoadThread(getMetarMaxAgeMin());
193     _metarLoadThread->start();
194 #endif
195 }
196
197 NoaaMetarRealWxController::~NoaaMetarRealWxController()
198 {
199 #if defined(ENABLE_THREADS)
200     if( _metarLoadThread ) {
201         MetarLoadRequest request("");
202         _metarLoadThread->requestMetar(request);
203         _metarLoadThread->join();
204         delete _metarLoadThread;
205     }
206 #endif // ENABLE_THREADS
207 }
208
209 void NoaaMetarRealWxController::update( bool first, double dt )
210 {
211     _metarTimeToLive -= dt;
212     _positionTimeToLive -= dt;
213     _minimumRequestInterval -= dt;
214
215     bool valid = _metarValidNode->getBoolValue();
216     string stationId = valid ? _metarStationIdNode->getStringValue() : "";
217
218     if( first ) _metarTimeToLive = 0.0;
219
220     if( _metarTimeToLive <= 0.0 ) {
221         valid = false;
222         _metarTimeToLive = 900;
223         _positionTimeToLive = 0;
224     }
225
226     if( _positionTimeToLive <= 0.0 || valid == false ) {
227         _positionTimeToLive = 60.0;
228
229         SGGeod pos = SGGeod::fromDeg(_longitude_n->getDoubleValue(), _latitude_n->getDoubleValue());
230
231         FGAirport * nearestAirport = FGAirport::findClosest(pos, 10000.0, MetarAirportFilter::instance() );
232         if( nearestAirport == NULL ) {
233             SG_LOG(SG_ALL,SG_WARN,"RealWxController::update can't find airport with METAR within 10000NM"  );
234             return;
235         }
236
237         if( stationId != nearestAirport->ident() ) {
238             valid = false;
239             stationId = nearestAirport->ident();
240         }
241
242     }
243
244     if( !valid ) {
245         if( _minimumRequestInterval <= 0 && stationId.length() > 0 ) {
246             MetarLoadRequest request( stationId );
247             // load the first metar in the foreground to make sure a metar is received
248             // before the automatic runway selection code runs. All subsequent calls
249             // run in the background
250             _metarLoadThread->requestMetar( request, !first );
251             _minimumRequestInterval = 10;
252         }
253     }
254
255     if( _metarLoadThread->hasMetar() ) {
256         string metar = _metarLoadThread->getMetar();
257         SG_LOG( SG_ALL, SG_ALERT, "NoaaMetarRwalWxController::update() received METAR " << metar );
258         _metarDataNode->setStringValue( metar );
259     }
260
261
262 }
263
264 /* -------------------------------------------------------------------------------- */
265
266 #if defined(ENABLE_THREADS)
267 NoaaMetarRealWxController::MetarLoadThread::MetarLoadThread( long maxAge ) :
268   _maxAge(maxAge)
269 {
270 }
271
272 void NoaaMetarRealWxController::MetarLoadThread::requestMetar( const MetarLoadRequest & metarRequest, bool background )
273 {
274     if( background ) {
275         if( _requestQueue.size() > 10 ) {
276             SG_LOG(SG_ALL,SG_ALERT,
277                 "NoaaMetarRealWxController::MetarLoadThread::requestMetar() more than 10 outstanding METAR requests, dropping " 
278                 << metarRequest._stationId );
279             return;
280         }
281
282         _requestQueue.push( metarRequest );
283     } else {
284         fetch( metarRequest );
285     }
286 }
287
288 void NoaaMetarRealWxController::MetarLoadThread::run()
289 {
290     for( ;; ) {
291         const MetarLoadRequest request = _requestQueue.pop();
292
293         if( request._stationId.size() == 0 )
294             break;
295
296         fetch( request );
297     }
298 }
299
300 void NoaaMetarRealWxController::MetarLoadThread::fetch( const MetarLoadRequest & request )
301 {
302    SGSharedPtr<FGMetar> result = NULL;
303
304     try {
305         result = new FGMetar( request._stationId, request._proxyHost, request._proxyPort, request._proxyAuth );
306     } catch (const sg_io_exception& e) {
307         SG_LOG( SG_GENERAL, SG_WARN, "NoaaMetarRealWxController::fetchMetar(): can't get METAR for " 
308                                     << request._stationId << ":" << e.getFormattedMessage().c_str() );
309         return;
310     }
311
312     string reply = result->getData();
313     std::replace(reply.begin(), reply.end(), '\n', ' ');
314     string metar = simgear::strutils::strip( reply );
315
316     if( metar.empty() ) {
317         SG_LOG( SG_GENERAL, SG_WARN, "NoaaMetarRealWxController::fetchMetar(): dropping empty METAR for " 
318                                     << request._stationId );
319         return;
320     }
321
322     if( _maxAge && result->getAge_min() > _maxAge ) {
323         SG_LOG( SG_GENERAL, SG_ALERT, "NoaaMetarRealWxController::fetchMetar(): dropping outdated METAR " 
324                                      << metar );
325         return;
326     }
327
328     _responseQueue.push( metar );
329 }
330 #endif
331
332 /* -------------------------------------------------------------------------------- */
333
334 RealWxController * RealWxController::createInstance( SGPropertyNode_ptr rootNode )
335 {
336 //    string dataSource = rootNode->getStringValue("data-source", "noaa" );
337 //    if( dataSource == "nwx" ) {
338 //      return new NwxMetarRealWxController( rootNode );
339 //    } else {
340       return new NoaaMetarRealWxController( rootNode );
341 //    }
342 }
343
344 /* -------------------------------------------------------------------------------- */
345
346 } // namespace Environment