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