X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftsync%2Fterrasync.cxx;h=e4ae8ff8ad0a7ce35f7ee6a395e9f72e12a67abb;hb=ae0b8eb3b3943690d0788dde316b1c152390f1fd;hp=9a35afcef2cdf9ed67a14ed9945183febb2f8496;hpb=cc0679983845866eecf08d68806d5ec18a2b1550;p=simgear.git diff --git a/simgear/scene/tsync/terrasync.cxx b/simgear/scene/tsync/terrasync.cxx index 9a35afce..e4ae8ff8 100644 --- a/simgear/scene/tsync/terrasync.cxx +++ b/simgear/scene/tsync/terrasync.cxx @@ -45,6 +45,7 @@ #include // atoi() atof() abs() system() #include // signal() +#include #include #include @@ -58,9 +59,7 @@ #include #include #include -#include #include -#include #ifdef HAVE_SVN_CLIENT_H # ifdef HAVE_LIBSVN_CLIENT_1 @@ -86,6 +85,7 @@ #endif using namespace simgear; +using namespace std; const char* rsync_cmd = "rsync --verbose --archive --delete --perms --owner --group"; @@ -93,6 +93,14 @@ const char* rsync_cmd = const char* svn_options = "checkout -q"; +namespace UpdateInterval +{ + // interval in seconds to allow an update to repeat after a successful update (=daily) + static const double SuccessfulAttempt = 24*60*60; + // interval in seconds to allow another update after a failed attempt (10 minutes) + static const double FailedAttempt = 10*60; +}; + typedef map CompletedTiles; /////////////////////////////////////////////////////////////////////////////// @@ -104,13 +112,18 @@ string stripPath(string path) path = simgear::strutils::strip(path); int slen = path.length(); while ((slen>0)&& - (path[slen-1]=='/')||(path[slen-1]=='\\')) + ((path[slen-1]=='/')||(path[slen-1]=='\\'))) { slen--; } return path.substr(0,slen); } +bool hasWhitespace(string path) +{ + return path.find(' ')!=string::npos; +} + /////////////////////////////////////////////////////////////////////////////// // WaitingTile //////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -126,7 +139,7 @@ public: /////////////////////////////////////////////////////////////////////////////// // SGTerraSync::SvnThread ///////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -class SGTerraSync::SvnThread : public OpenThreads::Thread +class SGTerraSync::SvnThread : public SGThread { public: SvnThread(); @@ -147,6 +160,7 @@ public: void setLocalDir(string dir) { _local_dir = stripPath(dir);} string getLocalDir() { return _local_dir;} void setUseSvn(bool use_svn) { _use_svn = use_svn;} + void setAllowedErrorCount(int errors) {_allowed_errors = errors;} #ifdef HAVE_SVN_CLIENT_H void setUseBuiltin(bool built_in) { _use_built_in = built_in;} @@ -160,6 +174,7 @@ public: volatile int _updated_tile_count; volatile int _success_count; volatile int _consecutive_errors; + volatile int _allowed_errors; private: virtual void run(); @@ -207,6 +222,7 @@ SGTerraSync::SvnThread::SvnThread() : _updated_tile_count(0), _success_count(0), _consecutive_errors(0), + _allowed_errors(6), #ifdef HAVE_SVN_CLIENT_H _use_built_in(true), #endif @@ -275,6 +291,7 @@ bool SGTerraSync::SvnThread::start() _stalled = true; return false; } + #ifdef HAVE_SVN_CLIENT_H _use_svn |= _use_built_in; #endif @@ -287,7 +304,6 @@ bool SGTerraSync::SvnThread::start() _stalled = true; return false; } - if ((!_use_svn)&&(_rsync_server=="")) { SG_LOG(SG_TERRAIN,SG_ALERT, @@ -329,7 +345,7 @@ bool SGTerraSync::SvnThread::start() << status << "Directory: '" << _local_dir << "'."); - OpenThreads::Thread::start(); + SGThread::start(); return true; } @@ -374,20 +390,19 @@ bool SGTerraSync::SvnThread::syncTreeInternal(const char* dir) return false; } - char command[512]; - char dest_base_dir[512]; - snprintf( command, 512, - "%s/%s", _svn_server.c_str(), dir); - snprintf( dest_base_dir, 512, - "%s/%s", _local_dir.c_str(), dir); + ostringstream command; + command << _svn_server << "/" << dir; + + ostringstream dest_base_dir; + dest_base_dir << _local_dir << "/" << dir; apr_pool_t *subpool = svn_pool_create(_svn_pool); svn_error_t *err = NULL; #if (SVN_VER_MINOR >= 5) err = svn_client_checkout3(NULL, - command, - dest_base_dir, + command.str().c_str(), + dest_base_dir.str().c_str(), _svn_rev_peg, _svn_rev, svn_depth_infinity, @@ -398,8 +413,8 @@ bool SGTerraSync::SvnThread::syncTreeInternal(const char* dir) #else // version 1.4 API err = svn_client_checkout2(NULL, - command, - dest_base_dir, + command.str().c_str(), + dest_base_dir.str().c_str(), _svn_rev_peg, _svn_rev, 1, // recurse=true - same as svn_depth_infinity for checkout3 above @@ -423,7 +438,7 @@ bool SGTerraSync::SvnThread::syncTreeInternal(const char* dir) err->message << " (code " << err->apr_err << ")."); svn_error_clear(err); // try to clean up - err = svn_client_cleanup(dest_base_dir, + err = svn_client_cleanup(dest_base_dir.str().c_str(), _svn_ctx,subpool); if (!err) { @@ -443,22 +458,48 @@ bool SGTerraSync::SvnThread::syncTreeInternal(const char* dir) bool SGTerraSync::SvnThread::syncTreeExternal(const char* dir) { - int rc; - char command[512]; + ostringstream buf; + SGPath localPath( _local_dir ); + localPath.append( dir ); + if (_use_svn) { - snprintf( command, 512, - "\"%s\" %s %s/%s \"%s/%s\"", _svn_command.c_str(), svn_options, - _svn_server.c_str(), dir, - _local_dir.c_str(), dir ); + buf << "\"" << _svn_command << "\" " + << svn_options << " " + << "\"" << _svn_server << "/" << dir << "\" " + << "\"" << localPath.str_native() << "\""; } else { - snprintf( command, 512, - "%s %s/%s/ \"%s/%s/\"", rsync_cmd, - _rsync_server.c_str(), dir, - _local_dir.c_str(), dir ); + buf << rsync_cmd << " " + << "\"" << _rsync_server << "/" << dir << "/\" " + << "\"" << localPath.str_native() << "/\""; } + + string command; +#ifdef SG_WINDOWS + // windows command line parsing is just lovely... + // to allow white spaces, the system call needs this: + // ""C:\Program Files\something.exe" somearg "some other arg"" + // Note: whitespace strings quoted by a pair of "" _and_ the + // entire string needs to be wrapped by "" too. + // The svn url needs forward slashes (/) as a path separator while + // the local path needs windows-native backslash as a path separator. + command = "\"" + buf.str() + "\""; +#else + command = buf.str(); +#endif SG_LOG(SG_TERRAIN,SG_DEBUG, "sync command '" << command << "'"); - rc = system( command ); + +#ifdef SG_WINDOWS + // tbd: does Windows support "popen"? + int rc = system( command.c_str() ); +#else + FILE* pipe = popen( command.c_str(), "r"); + int rc=-1; + // wait for external process to finish + if (pipe) + rc = pclose(pipe); +#endif + if (rc) { SG_LOG(SG_TERRAIN,SG_ALERT, @@ -482,7 +523,7 @@ void SGTerraSync::SvnThread::run() _completedTiles.find( next._dir ); time_t now = time(0); if ((ii == _completedTiles.end())|| - ((ii->second + 60*60*24) < now )) + (ii->second < now )) { bool isNewDirectory = false; @@ -491,6 +532,7 @@ void SGTerraSync::SvnThread::run() { _consecutive_errors++; _fail_count++; + _completedTiles[ next._dir ] = now + UpdateInterval::FailedAttempt; } else { @@ -510,12 +552,13 @@ void SGTerraSync::SvnThread::run() _is_dirty = true; } } + _completedTiles[ next._dir ] = now + UpdateInterval::SuccessfulAttempt; } _busy = false; - _completedTiles[ next._dir ] = now; } - if (_consecutive_errors >= 5) + if ((_allowed_errors >= 0)&& + (_consecutive_errors >= _allowed_errors)) { _stalled = true; _stop = true; @@ -541,16 +584,18 @@ int SGTerraSync::SvnThread::svnClientSetup(void) // not supported under msvc 7.1 ( code inside svn_cmdline_init ) if (svn_cmdline_init("terrasync", 0) != EXIT_SUCCESS) return EXIT_FAILURE; + + // revert locale setting + setlocale(LC_ALL,"C"); #else + /* svn_cmdline_init configures the locale. Setup environment to ensure the + * default "C" locale remains active, since fgfs isn't locale aware - especially + * requires "." as decimal point in strings containing floating point varibales. */ + setenv("LC_ALL", "C", 1); + if (svn_cmdline_init("terrasync", stderr) != EXIT_SUCCESS) return EXIT_FAILURE; #endif - /* Oh no! svn_cmdline_init configures the locale - affecting numeric output - * formats (i.e. sprintf("%f", ...)). fgfs relies on "C" locale in many places - * (including assumptions on required sprintf buffer sizes). Things go horribly - * wrong when the locale is changed to anything else but "C". Might be enough to - * revert LC_NUMERIC locale - but we'll do a complete revert for now...*/ - setlocale(LC_ALL,"C"); apr_pool_t *pool; apr_pool_create(&pool, NULL); @@ -627,7 +672,8 @@ SGTerraSync::SGTerraSync(SGPropertyNode_ptr root) : last_lat(NOWHERE), last_lon(NOWHERE), _terraRoot(root->getNode("/sim/terrasync",true)), - _tile_cache(NULL) + _refreshCb(NULL), + _userCbData(NULL) { _svnThread = new SvnThread(); } @@ -641,8 +687,8 @@ SGTerraSync::~SGTerraSync() void SGTerraSync::init() { - _refresh_display = _terraRoot->getNode("refresh-display",true); - _terraRoot->getNode("built-in-svn-available",true)->setBoolValue(svn_built_in_available); + _refreshDisplay = _terraRoot->getNode("refresh-display",true); + _terraRoot->setBoolValue("built-in-svn-available",svn_built_in_available); reinit(); } @@ -660,22 +706,32 @@ void SGTerraSync::reinit() _svnThread->setSvnServer(_terraRoot->getStringValue("svn-server","")); _svnThread->setRsyncServer(_terraRoot->getStringValue("rsync-server","")); _svnThread->setLocalDir(_terraRoot->getStringValue("scenery-dir","")); + _svnThread->setAllowedErrorCount(_terraRoot->getIntValue("max-errors",5)); #ifdef HAVE_SVN_CLIENT_H _svnThread->setUseBuiltin(_terraRoot->getBoolValue("use-built-in-svn",true)); #else - _terraRoot->getNode("use-built-in-svn",true)->setBoolValue(false); + _terraRoot->setBoolValue("use-built-in-svn",false); #endif _svnThread->setUseSvn(_terraRoot->getBoolValue("use-svn",true)); _svnThread->setExtSvnUtility(_terraRoot->getStringValue("ext-svn-utility","svn")); if (_svnThread->start()) + { syncAirportsModels(); + if (last_lat != NOWHERE && last_lon != NOWHERE) + { + // reschedule most recent position + int lat = last_lat; + int lon = last_lon; + last_lat = NOWHERE; + last_lon = NOWHERE; + schedulePosition(lat, lon); + } + } } - _stalled_node->setBoolValue(_svnThread->_stalled); - last_lat = NOWHERE; - last_lon = NOWHERE; + _stalledNode->setBoolValue(_svnThread->_stalled); } void SGTerraSync::bind() @@ -693,9 +749,9 @@ void SGTerraSync::bind() _terraRoot->getNode("use-built-in-svn", true)->setAttribute(SGPropertyNode::USERARCHIVE,false); _terraRoot->getNode("use-svn", true)->setAttribute(SGPropertyNode::USERARCHIVE,false); // stalled is used as a signal handler (to connect listeners triggering GUI pop-ups) - _stalled_node = _terraRoot->getNode("stalled", true); - _stalled_node->setBoolValue(_svnThread->_stalled); - _stalled_node->setAttribute(SGPropertyNode::PRESERVE,true); + _stalledNode = _terraRoot->getNode("stalled", true); + _stalledNode->setBoolValue(_svnThread->_stalled); + _stalledNode->setAttribute(SGPropertyNode::PRESERVE,true); } void SGTerraSync::unbind() @@ -722,10 +778,10 @@ void SGTerraSync::update(double) SG_LOG(SG_TERRAIN,SG_ALERT, "Automatic scenery download/synchronization has stopped."); } - _stalled_node->setBoolValue(_svnThread->_stalled); + _stalledNode->setBoolValue(_svnThread->_stalled); } - if (!_refresh_display->getBoolValue()) + if (!_refreshDisplay->getBoolValue()) return; while (_svnThread->hasNewTiles()) @@ -742,7 +798,7 @@ void SGTerraSync::update(double) void SGTerraSync::refreshScenery(SGPath path,const string& relativeDir) { // find tiles to be refreshed - if (_tile_cache) + if (_refreshCb) { path.append(relativeDir); if (path.exists()) @@ -756,7 +812,7 @@ void SGTerraSync::refreshScenery(SGPath path,const string& relativeDir) { // reload scenery tile long index = atoi(tileList[i].file().c_str()); - _tile_cache->refresh_tile(index); + _refreshCb(_userCbData, index); } } } @@ -764,27 +820,25 @@ void SGTerraSync::refreshScenery(SGPath path,const string& relativeDir) bool SGTerraSync::isIdle() {return _svnThread->isIdle();} -void SGTerraSync::setTileCache(TileCache* tile_cache) +void SGTerraSync::setTileRefreshCb(SGTerraSyncCallback refreshCb, void* userCbData) { - _tile_cache = tile_cache; + _refreshCb = refreshCb; + _userCbData = userCbData; } void SGTerraSync::syncAirportsModels() { - char synced_other; - for ( synced_other = 'K'; synced_other <= 'Z'; synced_other++ ) + static const char* bounds = "MZAJKL"; // airport sync order: K-L, A-J, M-Z + // note "request" method uses LIFO order, i.e. processes most recent request first + for( unsigned i = 0; i < strlen(bounds)/2; i++ ) { - char dir[512]; - snprintf( dir, 512, "Airports/%c", synced_other ); - WaitingTile w(dir,false); - _svnThread->request( w ); - } - for ( synced_other = 'A'; synced_other <= 'J'; synced_other++ ) - { - char dir[512]; - snprintf( dir, 512, "Airports/%c", synced_other ); - WaitingTile w(dir,false); - _svnThread->request( w ); + for ( char synced_other = bounds[2*i]; synced_other <= bounds[2*i+1]; synced_other++ ) + { + ostringstream dir; + dir << "Airports/" << synced_other; + WaitingTile w(dir.str(),false); + _svnThread->request( w ); + } } WaitingTile w("Models",false); _svnThread->request( w ); @@ -827,12 +881,11 @@ void SGTerraSync::syncArea( int lat, int lon ) bool refresh=true; for (const char** tree = &terrainobjects[0]; *tree; tree++) { - char dir[512]; - snprintf( dir, 512, "%s/%c%03d%c%02d/%c%03d%c%02d", - *tree, - EW, abs(baselon), NS, abs(baselat), - EW, abs(lon), NS, abs(lat) ); - WaitingTile w(dir,refresh); + ostringstream dir; + dir << *tree << "/" << setfill('0') + << EW << setw(3) << abs(baselon) << NS << setw(2) << abs(baselat) << "/" + << EW << setw(3) << abs(lon) << NS << setw(2) << abs(lat); + WaitingTile w(dir.str(),refresh); _svnThread->request( w ); refresh=false; } @@ -870,46 +923,49 @@ void SGTerraSync::syncAreas( int lat, int lon, int lat_dir, int lon_dir ) bool SGTerraSync::schedulePosition(int lat, int lon) { + bool Ok = false; + // Ignore messages where the location does not change if ( lat != last_lat || lon != last_lon ) { - SG_LOG(SG_TERRAIN,SG_DEBUG, "Requesting scenery update for position " << - lat << "," << lon); - int lat_dir, lon_dir, dist; - if ( last_lat == NOWHERE || last_lon == NOWHERE ) + if (_svnThread->_running) { - lat_dir = lon_dir = 0; - } else - { - dist = lat - last_lat; - if ( dist != 0 ) - { - lat_dir = dist / abs(dist); - } - else - { - lat_dir = 0; - } - dist = lon - last_lon; - if ( dist != 0 ) + SG_LOG(SG_TERRAIN,SG_DEBUG, "Requesting scenery update for position " << + lat << "," << lon); + int lat_dir=0; + int lon_dir=0; + if ( last_lat != NOWHERE && last_lon != NOWHERE ) { - lon_dir = dist / abs(dist); - } else - { - lon_dir = 0; + int dist = lat - last_lat; + if ( dist != 0 ) + { + lat_dir = dist / abs(dist); + } + else + { + lat_dir = 0; + } + dist = lon - last_lon; + if ( dist != 0 ) + { + lon_dir = dist / abs(dist); + } else + { + lon_dir = 0; + } } - } - SG_LOG(SG_TERRAIN,SG_DEBUG, "Scenery update for " << - "lat = " << lat << ", lon = " << lon << - ", lat_dir = " << lat_dir << ", " << - "lon_dir = " << lon_dir); - - syncAreas( lat, lon, lat_dir, lon_dir ); + SG_LOG(SG_TERRAIN,SG_DEBUG, "Scenery update for " << + "lat = " << lat << ", lon = " << lon << + ", lat_dir = " << lat_dir << ", " << + "lon_dir = " << lon_dir); + syncAreas( lat, lon, lat_dir, lon_dir ); + Ok = true; + } last_lat = lat; last_lon = lon; - return true; } - return false; + + return Ok; }