From 2c24b1da17f15e01b17c38ceb0fd4b1aa1ec5abb Mon Sep 17 00:00:00 2001 From: Mathias Froehlich Date: Sun, 7 Aug 2011 11:12:45 +0200 Subject: [PATCH 1/8] Use the bv tree leafs for altitude queries. Make use ot the bounding volume tree nodes already present in the scenegraph for the ground cache for scenery intersection and elevation queries. --- src/Scenery/scenery.cxx | 225 ++++++++++++++++++++++++++++------------ 1 file changed, 158 insertions(+), 67 deletions(-) diff --git a/src/Scenery/scenery.cxx b/src/Scenery/scenery.cxx index 58bd4db56..ebf36ae28 100644 --- a/src/Scenery/scenery.cxx +++ b/src/Scenery/scenery.cxx @@ -28,8 +28,12 @@ #include #include +#include +#include +#include +#include +#include #include -#include #include #include @@ -38,6 +42,8 @@ #include #include #include +#include +#include #include
#include
@@ -70,6 +76,138 @@ public: } }; +class FGSceneryIntersect : public osg::NodeVisitor { +public: + FGSceneryIntersect(const SGLineSegmentd& lineSegment, + const osg::Node* skipNode) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN), + _lineSegment(lineSegment), + _skipNode(skipNode), + _material(0), + _haveHit(false) + { } + + bool getHaveHit() const + { return _haveHit; } + const SGLineSegmentd& getLineSegment() const + { return _lineSegment; } + const SGMaterial* getMaterial() const + { return _material; } + + virtual void apply(osg::Node& node) + { + if (&node == _skipNode) + return; + if (!testBoundingSphere(node.getBound())) + return; + + addBoundingVolume(node); + } + + virtual void apply(osg::Group& group) + { + if (&group == _skipNode) + return; + if (!testBoundingSphere(group.getBound())) + return; + + traverse(group); + addBoundingVolume(group); + } + + virtual void apply(osg::Transform& transform) + { handleTransform(transform); } + virtual void apply(osg::Camera& camera) + { + if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER) + return; + handleTransform(camera); + } + virtual void apply(osg::CameraView& transform) + { handleTransform(transform); } + virtual void apply(osg::MatrixTransform& transform) + { handleTransform(transform); } + virtual void apply(osg::PositionAttitudeTransform& transform) + { handleTransform(transform); } + +private: + void handleTransform(osg::Transform& transform) + { + if (&transform == _skipNode) + return; + // Hmm, may be this needs to be refined somehow ... + if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF) + return; + + if (!testBoundingSphere(transform.getBound())) + return; + + osg::Matrix inverseMatrix; + if (!transform.computeWorldToLocalMatrix(inverseMatrix, this)) + return; + osg::Matrix matrix; + if (!transform.computeLocalToWorldMatrix(matrix, this)) + return; + + SGLineSegmentd lineSegment = _lineSegment; + bool haveHit = _haveHit; + const SGMaterial* material = _material; + + _haveHit = false; + _lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr())); + + addBoundingVolume(transform); + traverse(transform); + + if (_haveHit) { + _lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr())); + } else { + _lineSegment = lineSegment; + _material = material; + _haveHit = haveHit; + } + } + + simgear::BVHNode* getNodeBoundingVolume(osg::Node& node) + { + SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node); + if (!userData) + return 0; + return userData->getBVHNode(); + } + void addBoundingVolume(osg::Node& node) + { + simgear::BVHNode* bvNode = getNodeBoundingVolume(node); + if (!bvNode) + return; + + // Find ground intersection on the bvh nodes + simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment, + 0/*startTime*/); + bvNode->accept(lineSegmentVisitor); + if (!lineSegmentVisitor.empty()) { + _lineSegment = lineSegmentVisitor.getLineSegment(); + _material = lineSegmentVisitor.getMaterial(); + _haveHit = true; + } + } + + bool testBoundingSphere(const osg::BoundingSphere& bound) const + { + if (!bound.valid()) + return false; + + SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius); + return intersects(_lineSegment, sphere); + } + + SGLineSegmentd _lineSegment; + const osg::Node* _skipNode; + + const SGMaterial* _material; + bool _haveHit; +}; + // Scenery Management system FGScenery::FGScenery() { @@ -138,43 +276,20 @@ FGScenery::get_elevation_m(const SGGeod& geod, double& alt, SGGeod geodEnd = geod; geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000)); SGVec3d end = SGVec3d::fromGeod(geodEnd); - - osgUtil::IntersectVisitor intersectVisitor; + + FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom); intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT); - osg::ref_ptr lineSegment; - lineSegment = new osg::LineSegment(toOsg(start), toOsg(end)); - intersectVisitor.addLineSegment(lineSegment.get()); get_scene_graph()->accept(intersectVisitor); - bool hits = false; - if (intersectVisitor.hits()) { - int nHits = intersectVisitor.getNumHits(lineSegment.get()); - alt = -SGLimitsd::max(); - for (int i = 0; i < nHits; ++i) { - const osgUtil::Hit& hit - = intersectVisitor.getHitList(lineSegment.get())[i]; - if (butNotFrom && - std::find(hit.getNodePath().begin(), hit.getNodePath().end(), - butNotFrom) != hit.getNodePath().end()) - continue; - // We might need the double variant of the intersection point. - // Thus we cannot use the float variant delivered by - // hit.getWorldIntersectPoint() but we have to redo that with osg::Vec3d. - osg::Vec3d point = hit.getLocalIntersectPoint(); - if (hit.getMatrix()) - point = point*(*hit.getMatrix()); - SGGeod geod = SGGeod::fromCart(toSG(point)); - double elevation = geod.getElevationM(); - if (alt < elevation) { - alt = elevation; - hits = true; - if (material) - *material = SGMaterialLib::findMaterial(hit.getGeode()); - } - } - } + if (!intersectVisitor.getHaveHit()) + return false; - return hits; + geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd()); + alt = geod.getElevationM(); + if (material) + *material = intersectVisitor.getMaterial(); + + return true; } bool @@ -190,40 +305,16 @@ FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir, // computation of ground intersection. SGVec3d start = pos; SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ??? - - osgUtil::IntersectVisitor intersectVisitor; - intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT); - osg::ref_ptr lineSegment; - lineSegment = new osg::LineSegment(toOsg(start), toOsg(end)); - intersectVisitor.addLineSegment(lineSegment.get()); - get_scene_graph()->accept(intersectVisitor); - bool hits = false; - if (intersectVisitor.hits()) { - int nHits = intersectVisitor.getNumHits(lineSegment.get()); - double dist = SGLimitsd::max(); - for (int i = 0; i < nHits; ++i) { - const osgUtil::Hit& hit - = intersectVisitor.getHitList(lineSegment.get())[i]; - if (butNotFrom && - std::find(hit.getNodePath().begin(), hit.getNodePath().end(), - butNotFrom) != hit.getNodePath().end()) - continue; - // We might need the double variant of the intersection point. - // Thus we cannot use the float variant delivered by - // hit.getWorldIntersectPoint() but we have to redo that with osg::Vec3d. - osg::Vec3d point = hit.getLocalIntersectPoint(); - if (hit.getMatrix()) - point = point*(*hit.getMatrix()); - double newdist = length(start - toSG(point)); - if (newdist < dist) { - dist = newdist; - nearestHit = toSG(point); - hits = true; - } - } - } - return hits; + FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom); + intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT); + get_scene_graph()->accept(intersectVisitor); + + if (!intersectVisitor.getHaveHit()) + return false; + + nearestHit = intersectVisitor.getLineSegment().getEnd(); + return true; } bool FGScenery::scenery_available(const SGGeod& position, double range_m) From 02cf9774e814b91f78b165de8f150dada3941658 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sun, 7 Aug 2011 11:42:39 +0100 Subject: [PATCH 2/8] Add property binding for 3D cloud wrapping. Note that this relies on SimGear commmit 410c5ae --- src/Environment/environment_mgr.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Environment/environment_mgr.cxx b/src/Environment/environment_mgr.cxx index 0f1b201df..2eaaa8e7f 100644 --- a/src/Environment/environment_mgr.cxx +++ b/src/Environment/environment_mgr.cxx @@ -219,6 +219,10 @@ FGEnvironmentMgr::bind () &SGSky::get_3dCloudVisRange, &SGSky::set_3dCloudVisRange); + _tiedProperties.Tie("clouds3d-wrap", thesky, + &SGSky::get_3dCloudWrap, + &SGSky::set_3dCloudWrap); + // _tiedProperties.Tie("lightning-enable", &sgEnviro, // &SGEnviro::get_lightning_enable_state, // &SGEnviro::set_lightning_enable_state); From d3c06bd19b785bbd2a09bed27de66bf721ad7504 Mon Sep 17 00:00:00 2001 From: ThorstenB Date: Sun, 7 Aug 2011 18:24:13 +0200 Subject: [PATCH 3/8] #389: NumPad keys not working when NumLock is off Map keys to something useful when NumLock is off (arrow keys etc), since keycode was completely ignored (out of range) otherwise --- src/Main/FGEventHandler.cxx | 39 ++++++++++++++++++++++++++++++------- src/Main/FGEventHandler.hxx | 1 + 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/Main/FGEventHandler.cxx b/src/Main/FGEventHandler.cxx index d6be62054..5599723fa 100644 --- a/src/Main/FGEventHandler.cxx +++ b/src/Main/FGEventHandler.cxx @@ -60,6 +60,20 @@ FGEventHandler::FGEventHandler() : numlockKeyMap[GUIEventAdapter::KEY_KP_Home] = '7'; numlockKeyMap[GUIEventAdapter::KEY_KP_Up] = '8'; numlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = '9'; + numlockKeyMap[GUIEventAdapter::KEY_KP_Delete] = '.'; + + // mapping when NumLock is off + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Insert] = PU_KEY_INSERT; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_End] = PU_KEY_END; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Down] = PU_KEY_DOWN; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Page_Down] = PU_KEY_PAGE_DOWN; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Left] = PU_KEY_LEFT; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Begin] = '5'; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Right] = PU_KEY_RIGHT; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Home] = PU_KEY_HOME; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Up] = PU_KEY_UP; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Page_Up] = PU_KEY_PAGE_UP; + noNumlockKeyMap[GUIEventAdapter::KEY_KP_Delete] = 127; for (int i = 0; i < 128; i++) release_keys[i] = i; @@ -259,7 +273,6 @@ void FGEventHandler::handleKey(const osgGA::GUIEventAdapter& ea, int& key, case GUIEventAdapter::KEY_F10: key = PU_KEY_F10; break; case GUIEventAdapter::KEY_F11: key = PU_KEY_F11; break; case GUIEventAdapter::KEY_F12: key = PU_KEY_F12; break; - case GUIEventAdapter::KEY_KP_Delete: key = '.'; break; case GUIEventAdapter::KEY_KP_Enter: key = '\r'; break; case GUIEventAdapter::KEY_KP_Add: key = '+'; break; case GUIEventAdapter::KEY_KP_Divide: key = '/'; break; @@ -268,18 +281,30 @@ void FGEventHandler::handleKey(const osgGA::GUIEventAdapter& ea, int& key, } osgGA::GUIEventAdapter::EventType eventType = ea.getEventType(); - std::map::iterator numPadIter = numlockKeyMap.find(key); - - if (numPadIter != numlockKeyMap.end()) { #ifdef __APPLE__ - // Num Lock is always true on Mac + // Num Lock is always true on Mac + std::map::iterator numPadIter = numlockKeyMap.find(key); + if (numPadIter != numlockKeyMap.end()) { key = numPadIter->second; + } #else - if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_NUM_LOCK) { + if (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_NUM_LOCK) + { + // NumLock on: map to numeric keys + std::map::iterator numPadIter = numlockKeyMap.find(key); + if (numPadIter != numlockKeyMap.end()) { key = numPadIter->second; } -#endif } + else + { + // NumLock off: map to PU arrow keys + std::map::iterator numPadIter = noNumlockKeyMap.find(key); + if (numPadIter != noNumlockKeyMap.end()) { + key = numPadIter->second; + } + } +#endif modifiers = osgToFGModifiers(ea.getModKeyMask()); currentModifiers = modifiers; diff --git a/src/Main/FGEventHandler.hxx b/src/Main/FGEventHandler.hxx index 080d5afa1..652b2b74d 100644 --- a/src/Main/FGEventHandler.hxx +++ b/src/Main/FGEventHandler.hxx @@ -113,6 +113,7 @@ protected: int statsType; int currentModifiers; std::map numlockKeyMap; + std::map noNumlockKeyMap; void handleKey(const osgGA::GUIEventAdapter& ea, int& key, int& modifiers); bool resizable; bool mouseWarped; From 3a17ef2f1a7e49b1ba39b30649e0f5d47ac8ef91 Mon Sep 17 00:00:00 2001 From: Frederic Bouvier Date: Sun, 7 Aug 2011 19:50:35 +0200 Subject: [PATCH 4/8] Get rid of plib to enumerate files. Get a list of files when the directory names contains UTF-8 characters. Doesn't fix issues #394 though because untarka functions should be utf-8 aware --- projects/VC90/fgadmin/fgadmin.vcproj | 8 ++--- utils/fgadmin/src/fgadmin_funcs.cxx | 49 ++++++++++++++++------------ 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/projects/VC90/fgadmin/fgadmin.vcproj b/projects/VC90/fgadmin/fgadmin.vcproj index cf34dc845..d4c9916db 100644 --- a/projects/VC90/fgadmin/fgadmin.vcproj +++ b/projects/VC90/fgadmin/fgadmin.vcproj @@ -65,7 +65,7 @@ /> -#include #include @@ -142,11 +141,12 @@ void FGAdminUI::update_install_box() { install_box->clear(); if ( source.length() ) { - ulDir *dir = ulOpenDir( source.c_str() ) ; - ulDirEnt *ent; - while ( dir != 0 && ( ent = ulReadDir( dir ) ) ) { + struct dirent **list; + int nb = fl_filename_list( source.c_str(), &list ); + for ( int i = 0; i < nb; ++i ) { // find base name of archive file char base[FL_PATH_MAX]; + dirent *ent = list[i]; strncpy( base, ent->d_name, FL_PATH_MAX ); const char *p = fl_filename_ext( base ); int offset, expected_length = 0; @@ -186,9 +186,10 @@ void FGAdminUI::update_install_box() { // cout << install.str() << " exists." << endl; } } + free( ent ); } + free( list ); - ulCloseDir( dir ); for ( set::iterator it = file_list.begin(); it != file_list.end(); ++it ) { install_box->add( it->c_str() ); } @@ -217,16 +218,18 @@ void FGAdminUI::update_remove_box() { set dir_list; for ( int i = 0; i < 2; i++ ) { if ( !path[i].empty() ) { - ulDir *dir = ulOpenDir( path[i].c_str() ) ; - ulDirEnt *ent; - while ( dir != 0 && ( ent = ulReadDir( dir ) ) ) { + dirent **list; + int nb = fl_filename_list( path[i].c_str(), &list ); + for ( int i = 0; i < nb; ++i ) { + dirent *ent = list[i]; if ( strlen(ent->d_name) == 7 && ( ent->d_name[0] == 'e' || ent->d_name[0] == 'w' ) && ( ent->d_name[4] == 'n' || ent->d_name[4] == 's' ) ) { dir_list.insert( ent->d_name ); } + free( ent ); } - ulCloseDir( dir ); + free( list ); } } @@ -279,23 +282,25 @@ void FGAdminUI::install_selected() { static unsigned long count_dir( const char *dir_name, bool top = true ) { unsigned long cnt = 0L; - ulDir *dir = ulOpenDir( dir_name ) ; - if ( dir ) { - ulDirEnt *ent; - while ( ent = ulReadDir( dir ) ) { + dirent **list; + int nb = fl_filename_list( dir_name, &list ); + if ( nb != 0 ) { + for ( int i = 0; i < nb; ++i ) { + dirent *ent = list[i]; if ( strcmp( ent->d_name, "." ) == 0 ) { // ignore "." } else if ( strcmp( ent->d_name, ".." ) == 0 ) { // ignore ".." - } else if ( ent->d_isdir ) { + } else if ( fl_filename_isdir( ent->d_name ) ) { SGPath child( dir_name ); child.append( ent->d_name ); cnt += count_dir( child.c_str(), false ); } else { cnt += 1; } + free( ent ); } - ulCloseDir( dir ); + free( list ); } else if ( top ) { string base = dir_name; size_t pos = base.rfind('/'); @@ -310,15 +315,16 @@ static unsigned long count_dir( const char *dir_name, bool top = true ) { } static void remove_dir( const char *dir_name, void (*step)(void*,int), void *data, bool top = true ) { - ulDir *dir = ulOpenDir( dir_name ) ; - if ( dir ) { - ulDirEnt *ent; - while ( ent = ulReadDir( dir ) ) { + dirent **list; + int nb = fl_filename_list( dir_name, &list ); + if ( nb != 0 ) { + for ( int i = 0; i < nb; ++i ) { + dirent *ent = list[i]; if ( strcmp( ent->d_name, "." ) == 0 ) { // ignore "." } else if ( strcmp( ent->d_name, ".." ) == 0 ) { // ignore ".." - } else if ( ent->d_isdir ) { + } else if ( fl_filename_isdir( ent->d_name ) ) { SGPath child( dir_name ); child.append( ent->d_name ); remove_dir( child.c_str(), step, data, false ); @@ -328,8 +334,9 @@ static void remove_dir( const char *dir_name, void (*step)(void*,int), void *dat unlink( child.c_str() ); if (step) step( data, 1 ); } + free( ent ); } - ulCloseDir( dir ); + free( list ); rmdir( dir_name ); } else if ( top ) { string base = dir_name; From db85ebff95b5e7bcb469d99ae25e062b733d090b Mon Sep 17 00:00:00 2001 From: Frederic Bouvier Date: Sun, 7 Aug 2011 20:02:09 +0200 Subject: [PATCH 5/8] Typo in project file --- projects/VC90/fgadmin/fgadmin.vcproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/VC90/fgadmin/fgadmin.vcproj b/projects/VC90/fgadmin/fgadmin.vcproj index d4c9916db..a7d8f4e09 100644 --- a/projects/VC90/fgadmin/fgadmin.vcproj +++ b/projects/VC90/fgadmin/fgadmin.vcproj @@ -309,7 +309,7 @@ /> Date: Sun, 7 Aug 2011 20:26:34 +0200 Subject: [PATCH 6/8] More work toward an international fgadmin --- utils/fgadmin/src/fgadmin_funcs.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/fgadmin/src/fgadmin_funcs.cxx b/utils/fgadmin/src/fgadmin_funcs.cxx index cb6d43fcb..c05b9e929 100644 --- a/utils/fgadmin/src/fgadmin_funcs.cxx +++ b/utils/fgadmin/src/fgadmin_funcs.cxx @@ -24,7 +24,6 @@ #include #include #include -#include #ifdef _WIN32 # include @@ -257,7 +256,7 @@ void FGAdminUI::install_selected() { SGPath file( source ); file.append( f ); struct stat info; - stat( file.str().c_str(), &info ); + fl_stat( file.str().c_str(), &info ); float old_max = progress->maximum(); progress->maximum( info.st_size ); progress_label = "Installing "; From fc38d6982312e9b9e1330c4cf0fc49cc49598b81 Mon Sep 17 00:00:00 2001 From: Mathias Froehlich Date: Mon, 8 Aug 2011 19:12:14 +0200 Subject: [PATCH 7/8] Fixes #397: YASim aircraft start at >32000ft instead of runway The usual 'modify code before checkin but past testing' Problem. Return the elevation of the intersection result instead of the elevation of the querys start point. --- src/Scenery/scenery.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Scenery/scenery.cxx b/src/Scenery/scenery.cxx index ebf36ae28..12c3ebb69 100644 --- a/src/Scenery/scenery.cxx +++ b/src/Scenery/scenery.cxx @@ -285,7 +285,7 @@ FGScenery::get_elevation_m(const SGGeod& geod, double& alt, return false; geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd()); - alt = geod.getElevationM(); + alt = geodEnd.getElevationM(); if (material) *material = intersectVisitor.getMaterial(); From 3c9f4ad73efa801cea29f3c6ab60e189a143ee48 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 8 Aug 2011 18:16:49 +0100 Subject: [PATCH 8/8] Switch RealWx to use simgear::HTTP class. Also tweak metar-proxy to send a HTTP status response. --- src/Environment/realwx_ctrl.cxx | 297 +++++++++++--------------------- utils/metarproxy/metarproxy | 2 + 2 files changed, 102 insertions(+), 197 deletions(-) diff --git a/src/Environment/realwx_ctrl.cxx b/src/Environment/realwx_ctrl.cxx index 621fb11c3..b3a55331d 100644 --- a/src/Environment/realwx_ctrl.cxx +++ b/src/Environment/realwx_ctrl.cxx @@ -31,14 +31,16 @@ #include
+#include + #include #include #include +#include +#include +#include + #include -#if defined(ENABLE_THREADS) -#include -#include -#endif using simgear::PropertyList; @@ -110,6 +112,7 @@ protected: simgear::TiedPropertyList _tiedProperties; typedef std::vector MetarPropertiesList; MetarPropertiesList _metarProperties; + }; /* -------------------------------------------------------------------------------- */ @@ -195,86 +198,95 @@ public: virtual void update (bool first, double delta_time_sec); virtual void shutdown (); - class MetarLoadRequest { - public: - MetarLoadRequest( const string & stationId ) : - _stationId(stationId), - _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; - }; + /** + * callback from MetarGetRequest when a download succeeds + */ + void gotMetar(const string& stationId, const string& metar); private: double _positionTimeToLive; double _requestTimer; -#if defined(ENABLE_THREADS) - 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 _requestQueue; - SGBlockingQueue _responseQueue; - }; - - MetarLoadThread * _metarLoadThread; -#endif + simgear::HTTP::Client _http; }; +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 ) : BasicRealWxController(rootNode), _positionTimeToLive(0.0), _requestTimer(0.0) { -#if defined(ENABLE_THREADS) - _metarLoadThread = new MetarLoadThread(getMetarMaxAgeMin()); - _metarLoadThread->start(); -#endif + string proxyHost(fgGetString("/sim/presets/proxy/host")); + int proxyPort(fgGetInt("/sim/presets/proxy/port")); + string proxyAuth(fgGetString("/sim/presets/proxy/auth")); + + if (!proxyHost.empty()) { + _http.setProxy(proxyHost, proxyPort, proxyAuth); + } } void NoaaMetarRealWxController::shutdown() { -#if defined(ENABLE_THREADS) - if( _metarLoadThread ) { - delete _metarLoadThread; - _metarLoadThread = NULL; - } -#endif // ENABLE_THREADS } NoaaMetarRealWxController::~NoaaMetarRealWxController() @@ -283,6 +295,8 @@ NoaaMetarRealWxController::~NoaaMetarRealWxController() void NoaaMetarRealWxController::update( bool first, double dt ) { + _http.update(); + _positionTimeToLive -= dt; _requestTimer -= dt; @@ -315,143 +329,32 @@ void NoaaMetarRealWxController::update( bool first, double dt ) if( _requestTimer <= 0.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(); - it != _metarProperties.end(); it++ ) { - - 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 << "'" ); + SG_LOG(SG_ALL, SG_INFO, + "NoaaMetarRealWxController::update(): spawning load request for station-id '" << stationId << "'" ); - MetarLoadRequest request( stationId ); - // load the metar for the nearest airport in the foreground if the fdm is uninitialized - // 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 ); - } + _http.makeRequest(new MetarGetRequest(this, stationId)); + } // of MetarProperties iteration } } -/* -------------------------------------------------------------------------------- */ - -#if defined(ENABLE_THREADS) -NoaaMetarRealWxController::MetarLoadThread::MetarLoadThread( long maxAge ) : - _maxAge(maxAge), - _minRequestInterval(2000), - _stop(false) +void NoaaMetarRealWxController::gotMetar(const string& stationId, const string& metar) { -} - -void NoaaMetarRealWxController::MetarLoadThread::requestMetar( const MetarLoadRequest & metarRequest, bool background ) -{ - if( background ) { - if( _requestQueue.size() > 10 ) { - SG_LOG(SG_ALL,SG_ALERT, - "NoaaMetarRealWxController::MetarLoadThread::requestMetar() more than 10 outstanding METAR requests, dropping " - << metarRequest._stationId ); - return; - } - - _requestQueue.push( metarRequest ); - } else { - fetch( metarRequest ); + SG_LOG( SG_ALL, SG_INFO, "NoaaMetarRwalWxController::update() received METAR for " << stationId << ": " << metar ); + BOOST_FOREACH(LiveMetarProperties* p, _metarProperties) { + if (p->getStationId() != stationId) + continue; + + p->setTimeToLive(900); + p->setMetar( metar ); } } -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 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 ) diff --git a/utils/metarproxy/metarproxy b/utils/metarproxy/metarproxy index ba03eb377..88320881f 100755 --- a/utils/metarproxy/metarproxy +++ b/utils/metarproxy/metarproxy @@ -441,6 +441,8 @@ sub serve() $metar .= sprintf "$icao %02d%02d%02dZ ", $day, $hour, $min; $metar .= $last_metar{$addr} || $METAR_DEFAULT; } + + print $client "HTTP/1.0 200 OK\015\012"; print $client "Content-Type: text/plain\015\012" . "X-MetarProxy: nasse Maus\015\012" . "\015\012"