X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FNavaids%2FNavDataCache.cxx;h=f6756613bd6c19130d5c308b44c36282add9852f;hb=5c659b3970e9a4542519e0d528016c08f7ecb6c1;hp=3cc8a255e3984f97eaac333cf485e3a5621b0b7c;hpb=11c00afaec8836a5f52bcf8832ff2d50e2f11523;p=flightgear.git diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 3cc8a255e..f6756613b 100644 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -23,11 +23,6 @@ # include "config.h" #endif -// to ensure compatability between sqlite3_int64 and PositionedID, -// force the type used by sqlite to match PositionedID explicitly -#define SQLITE_INT64_TYPE int64_t -#define SQLITE_UINT64_TYPE uint64_t - #include "NavDataCache.hxx" // std @@ -37,9 +32,23 @@ // boost #include -#include "sqlite3.h" + +#ifdef SYSTEM_SQLITE +// the standard sqlite3.h doesn't give a way to set SQLITE_UINT64_TYPE, +// so we have to hope sizeof(int64_t) matches sizeof(sqlite3_int64). +// otherwise things will go bad quickly. + #include "sqlite3.h" +#else +// to ensure compatability between sqlite3_int64 and PositionedID, +// force the type used by sqlite to match PositionedID explicitly +#define SQLITE_INT64_TYPE int64_t +#define SQLITE_UINT64_TYPE uint64_t + + #include "fg_sqlite3.h" +#endif // SimGear +#include #include #include #include @@ -49,9 +58,11 @@ #include #include
+#include
+#include
#include "markerbeacon.hxx" #include "navrecord.hxx" -#include +#include #include #include #include "fix.hxx" @@ -60,8 +71,10 @@ #include "PositionedOctree.hxx" #include #include +#include "poidb.hxx" #include #include +#include "CacheSchema.h" using std::string; @@ -71,8 +84,8 @@ using std::string; namespace { const int MAX_RETRIES = 10; -const int SCHEMA_VERSION = 7; -const int CACHE_SIZE_KBYTES= 16000; + +const int CACHE_SIZE_KBYTES= 32 * 1024; // bind a std::string to a sqlite statement. The std::string must live the // entire duration of the statement execution - do not pass a temporary @@ -197,6 +210,7 @@ public: outer(o), db(NULL), path(p), + readOnly(false), cacheHits(0), cacheMisses(0), transactionLevel(0), @@ -212,9 +226,15 @@ public: void init() { SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache at:" << path); - sqlite3_open_v2(path.c_str(), &db, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); - + + readOnly = fgGetBool("/sim/fghome-readonly", false); + + int openFlags = readOnly ? SQLITE_OPEN_READONLY : + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + // see http://code.google.com/p/flightgear-bugs/issues/detail?id=1055 + // for the UTF8 / path logic here + std::string pathUtf8 = simgear::strutils::convertWindowsLocal8BitToUtf8(path.str()); + sqlite3_open_v2(pathUtf8.c_str(), &db, openFlags, NULL); sqlite3_stmt_ptr checkTables = prepare("SELECT count(*) FROM sqlite_master WHERE name='properties'"); @@ -224,7 +244,7 @@ public: execSelect(checkTables); bool didCreate = false; - if (sqlite3_column_int(checkTables, 0) == 0) { + if (!readOnly && (sqlite3_column_int(checkTables, 0) == 0)) { SG_LOG(SG_NAVCACHE, SG_INFO, "will create tables"); initTables(); didCreate = true; @@ -280,6 +300,8 @@ public: SG_LOG(SG_NAVCACHE, SG_INFO, "NavDataCache integrity check took:" << st.elapsedMSec()); finalize(stmt); } + + bool isCachedFileModified(const SGPath& path, bool verbose); void callSqlite(int result, const string& sql) { @@ -408,116 +430,17 @@ public: execSelect(stmt); reset(stmt); } - + void initTables() { - runSQL("CREATE TABLE properties (" - "key VARCHAR," - "value VARCHAR" - ")"); - - runSQL("CREATE TABLE stat_cache (" - "path VARCHAR unique," - "stamp INT" - ")"); - - runSQL("CREATE TABLE positioned (" - "type INT," - "ident VARCHAR collate nocase," - "name VARCHAR collate nocase," - "airport INT64," - "lon FLOAT," - "lat FLOAT," - "elev_m FLOAT," - "octree_node INT," - "cart_x FLOAT," - "cart_y FLOAT," - "cart_z FLOAT" - ")"); - - runSQL("CREATE INDEX pos_octree ON positioned(octree_node)"); - runSQL("CREATE INDEX pos_ident ON positioned(ident collate nocase)"); - runSQL("CREATE INDEX pos_name ON positioned(name collate nocase)"); - // allow efficient querying of 'all ATIS at this airport' or - // 'all towers at this airport' - runSQL("CREATE INDEX pos_apt_type ON positioned(airport, type)"); - - runSQL("CREATE TABLE airport (" - "has_metar BOOL" - ")" - ); - - runSQL("CREATE TABLE comm (" - "freq_khz INT," - "range_nm INT" - ")" - ); - - runSQL("CREATE INDEX comm_freq ON comm(freq_khz)"); - - runSQL("CREATE TABLE runway (" - "heading FLOAT," - "length_ft FLOAT," - "width_m FLOAT," - "surface INT," - "displaced_threshold FLOAT," - "stopway FLOAT," - "reciprocal INT64," - "ils INT64" - ")" - ); - - runSQL("CREATE TABLE navaid (" - "freq INT," - "range_nm INT," - "multiuse FLOAT," - "runway INT64," - "colocated INT64" - ")" - ); - - runSQL("CREATE INDEX navaid_freq ON navaid(freq)"); - - runSQL("CREATE TABLE octree (children INT)"); - - runSQL("CREATE TABLE airway (" - "ident VARCHAR collate nocase," - "network INT" // high-level or low-level - ")"); - - runSQL("CREATE INDEX airway_ident ON airway(ident)"); - - runSQL("CREATE TABLE airway_edge (" - "network INT," - "airway INT64," - "a INT64," - "b INT64" - ")"); - - runSQL("CREATE INDEX airway_edge_from ON airway_edge(a)"); - - runSQL("CREATE TABLE taxi_node (" - "hold_type INT," - "on_runway BOOL," - "pushback BOOL" - ")"); - - runSQL("CREATE TABLE parking (" - "heading FLOAT," - "radius INT," - "gate_type VARCHAR," - "airlines VARCHAR," - "pushback INT64" - ")"); - - runSQL("CREATE TABLE groundnet_edge (" - "airport INT64," - "a INT64," - "b INT64" - ")"); - - runSQL("CREATE INDEX groundnet_edge_airport ON groundnet_edge(airport)"); - runSQL("CREATE INDEX groundnet_edge_from ON groundnet_edge(a)"); + string_list commands = simgear::strutils::split(SCHEMA_SQL, ";"); + BOOST_FOREACH(std::string sql, commands) { + if (sql.empty()) { + continue; + } + + runSQL(sql); + } // of commands in scheme loop } void prepareQueries() @@ -552,7 +475,7 @@ public: setRunwayReciprocal = prepare("UPDATE runway SET reciprocal=?2 WHERE rowid=?1"); setRunwayILS = prepare("UPDATE runway SET ils=?2 WHERE rowid=?1"); - updateRunwayThreshold = prepare("UPDATE runway SET heading=?2, displaced_threshold=?3, stopway=?4 WHERE rowid=?1"); + setNavaidColocated = prepare("UPDATE navaid SET colocated=?2 WHERE rowid=?1"); insertPositionedQuery = prepare("INSERT INTO positioned " "(type, ident, name, airport, lon, lat, elev_m, octree_node, " @@ -564,7 +487,6 @@ public: insertAirport = prepare("INSERT INTO airport (rowid, has_metar) VALUES (?, ?)"); insertNavaid = prepare("INSERT INTO navaid (rowid, freq, range_nm, multiuse, runway, colocated)" " VALUES (?1, ?2, ?3, ?4, ?5, ?6)"); - updateILS = prepare("UPDATE navaid SET multiuse=?2 WHERE rowid=?1"); insertCommStation = prepare("INSERT INTO comm (rowid, freq_khz, range_nm)" " VALUES (?, ?, ?)"); @@ -573,6 +495,8 @@ public: " VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"); runwayLengthFtQuery = prepare("SELECT length_ft FROM runway WHERE rowid=?1"); + removePOIQuery = prepare("DELETE FROM positioned WHERE type=?1 AND ident=?2"); + // query statement findClosestWithIdent = prepare("SELECT rowid FROM positioned WHERE ident=?1 " AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)"); @@ -612,6 +536,11 @@ public: sqlite3_bind_int(searchAirports, 2, FGPositioned::AIRPORT); sqlite3_bind_int(searchAirports, 3, FGPositioned::SEAPORT); + getAllAirports = prepare("SELECT ident, name FROM positioned WHERE type>=?1 AND type <=?2"); + sqlite3_bind_int(getAllAirports, 1, FGPositioned::AIRPORT); + sqlite3_bind_int(getAllAirports, 2, FGPositioned::SEAPORT); + + getAirportItemByIdent = prepare("SELECT rowid FROM positioned WHERE airport=?1 AND ident=?2 AND type=?3"); findAirportRunway = prepare("SELECT airport, rowid FROM positioned WHERE ident=?2 AND type=?3 AND airport=" @@ -696,7 +625,7 @@ public: } - FGPositioned* loadById(sqlite_int64 rowId); + FGPositioned* loadById(sqlite_int64 rowId, sqlite3_int64& aptId); FGAirport* loadAirport(sqlite_int64 rowId, FGPositioned::Type ty, @@ -733,7 +662,7 @@ public: PositionedID reciprocal = sqlite3_column_int64(loadRunwayStmt, 6); PositionedID ils = sqlite3_column_int64(loadRunwayStmt, 7); FGRunway* r = new FGRunway(rowId, apt, id, pos, heading, lengthM, widthM, - displacedThreshold, stopway, surface, false); + displacedThreshold, stopway, surface); if (reciprocal > 0) { r->setReciprocalRunway(reciprocal); @@ -782,12 +711,22 @@ public: } int rangeNm = sqlite3_column_int(loadNavaid, 0), - freq = sqlite3_column_int(loadNavaid, 1); + freq = sqlite3_column_int(loadNavaid, 1); double mulituse = sqlite3_column_double(loadNavaid, 2); - //sqlite3_int64 colocated = sqlite3_column_int64(loadNavaid, 4); + PositionedID colocated = sqlite3_column_int64(loadNavaid, 4); reset(loadNavaid); - - return new FGNavRecord(rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway); + + FGNavRecord* n = + (ty == FGPositioned::MOBILE_TACAN) + ? new FGMobileNavRecord + (rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway) + : new FGNavRecord + (rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway); + + if (colocated) + n->setColocatedDME(colocated); + + return n; } FGPositioned* loadParking(sqlite3_int64 rowId, @@ -850,7 +789,7 @@ public: return r; } - FGPositioned::List findAllByString(const string& s, const string& column, + FGPositionedList findAllByString(const string& s, const string& column, FGPositioned::Filter* filter, bool exact) { string query = s; @@ -876,7 +815,7 @@ public: sqlite3_bind_int(stmt, 3, filter->maxType()); } - FGPositioned::List result; + FGPositionedList result; // run the prepared SQL while (stepSelect(stmt)) { @@ -921,11 +860,20 @@ public: deferredOctreeUpdates.clear(); } + + void removePositionedWithIdent(FGPositioned::Type ty, const std::string& aIdent) + { + sqlite3_bind_int(removePOIQuery, 1, ty); + sqlite_bind_stdstring(removePOIQuery, 2, aIdent); + execUpdate(removePOIQuery); + reset(removePOIQuery); + } NavDataCache* outer; sqlite3* db; SGPath path; - + bool readOnly; + /// the actual cache of ID -> instances. This holds an owning reference, /// so once items are in the cache they will never be deleted until /// the cache drops its reference @@ -939,7 +887,7 @@ public: bool transactionAborted; sqlite3_stmt_ptr beginTransactionStmt, commitTransactionStmt, rollbackTransactionStmt; - SGPath aptDatPath, metarDatPath, navDatPath, fixDatPath, + SGPath aptDatPath, metarDatPath, navDatPath, fixDatPath, poiDatPath, carrierDatPath, airwayDatPath; sqlite3_stmt_ptr readPropertyQuery, writePropertyQuery, @@ -950,15 +898,16 @@ public: sqlite3_stmt_ptr insertPositionedQuery, insertAirport, insertTower, insertRunway, insertCommStation, insertNavaid; - sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS, - setAirportPos, updateRunwayThreshold, updateILS; + sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS, setNavaidColocated, + setAirportPos; + sqlite3_stmt_ptr removePOIQuery; sqlite3_stmt_ptr findClosestWithIdent; // octree (spatial index) related queries sqlite3_stmt_ptr getOctreeChildren, insertOctree, updateOctreeChildren, getOctreeLeafChildren; - sqlite3_stmt_ptr searchAirports; + sqlite3_stmt_ptr searchAirports, getAllAirports; sqlite3_stmt_ptr findCommByFreq, findNavsByFreq, findNavsByFreqNoPos, findNavaidForRunway; sqlite3_stmt_ptr getAirportItems, getAirportItemByIdent; @@ -992,9 +941,10 @@ public: std::auto_ptr rebuilder; }; - ////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// -FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid) +FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid, + sqlite3_int64& aptId) { sqlite3_bind_int64(loadPositioned, 1, rowid); @@ -1003,9 +953,10 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid) assert(rowid == sqlite3_column_int64(loadPositioned, 0)); FGPositioned::Type ty = (FGPositioned::Type) sqlite3_column_int(loadPositioned, 1); + PositionedID prowid = static_cast(rowid); string ident = (char*) sqlite3_column_text(loadPositioned, 2); string name = (char*) sqlite3_column_text(loadPositioned, 3); - sqlite3_int64 aptId = sqlite3_column_int64(loadPositioned, 4); + aptId = sqlite3_column_int64(loadPositioned, 4); double lon = sqlite3_column_double(loadPositioned, 5); double lat = sqlite3_column_double(loadPositioned, 6); double elev = sqlite3_column_double(loadPositioned, 7); @@ -1020,7 +971,7 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid) return loadAirport(rowid, ty, ident, name, pos); case FGPositioned::TOWER: - return new AirportTower(rowid, aptId, ident, pos); + return new AirportTower(prowid, aptId, ident, pos); case FGPositioned::RUNWAY: case FGPositioned::HELIPAD: @@ -1038,26 +989,18 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid) case FGPositioned::DME: case FGPositioned::TACAN: case FGPositioned::MOBILE_TACAN: - { - if (aptId > 0) { - FGAirport* apt = (FGAirport*) outer->loadById(aptId); - if (apt->validateILSData()) { - SG_LOG(SG_NAVCACHE, SG_INFO, "re-loaded ILS data for " << apt->ident()); - // queried data above is probably invalid, force us to go around again - // (the next time through, validateILSData will return false) - return outer->loadById(rowid); - } - } - return loadNav(rowid, ty, ident, name, pos); - } case FGPositioned::FIX: return new FGFix(rowid, ident, pos); case FGPositioned::WAYPOINT: + case FGPositioned::COUNTRY: + case FGPositioned::CITY: + case FGPositioned::TOWN: + case FGPositioned::VILLAGE: { - FGPositioned* wpt = new FGPositioned(rowid, FGPositioned::WAYPOINT, ident, pos); + FGPositioned* wpt = new FGPositioned(rowid, ty, ident, pos); return wpt; } @@ -1081,7 +1024,37 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid) return NULL; } } - + +bool NavDataCache::NavDataCachePrivate::isCachedFileModified(const SGPath& path, bool verbose) +{ + if (!path.exists()) { + throw sg_io_exception("isCachedFileModified: Missing file:" + path.str()); + } + + sqlite_bind_temp_stdstring(statCacheCheck, 1, path.str()); + bool isModified = true; + sgDebugPriority logLevel = verbose ? SG_WARN : SG_DEBUG; + if (execSelect(statCacheCheck)) { + time_t modtime = sqlite3_column_int64(statCacheCheck, 0); + time_t delta = std::labs(modtime - path.modTime()); + if (delta != 0) + { + SG_LOG(SG_NAVCACHE, logLevel, "NavCache: rebuild required for " << path << + ". Timestamps: " << modtime << " != " << path.modTime()); + } + else + { + SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: no rebuild required for " << path); + } + + isModified = (delta != 0); + } else { + SG_LOG(SG_NAVCACHE, logLevel, "NavCache: initial build required for " << path); + } + + reset(statCacheCheck); + return isModified; +} static NavDataCache* static_instance = NULL; @@ -1089,7 +1062,16 @@ NavDataCache::NavDataCache() { const int MAX_TRIES = 3; SGPath homePath(globals->get_fg_home()); - homePath.append("navdata.cache"); + + std::ostringstream os; + string_list versionParts = simgear::strutils::split(VERSION, "."); + if (versionParts.size() < 2) { + os << "navdata.cache"; + } else { + os << "navdata_" << versionParts[0] << "_" << versionParts[1] << ".cache"; + } + + homePath.append(os.str()); for (int t=0; t < MAX_TRIES; ++t) { try { @@ -1102,7 +1084,11 @@ NavDataCache::NavDataCache() SG_LOG(SG_NAVCACHE, t == 0 ? SG_WARN : SG_ALERT, "NavCache: init failed:" << e.what() << " (attempt " << t << ")"); d.reset(); - homePath.remove(); + + // only wipe the existing if not readonly + if (!fgGetBool("/sim/fghome-readonly", false)) { + homePath.remove(); + } } } // of retry loop @@ -1122,6 +1108,9 @@ NavDataCache::NavDataCache() d->fixDatPath = SGPath(globals->get_fg_root()); d->fixDatPath.append("Navaids/fix.dat.gz"); + + d->poiDatPath = SGPath(globals->get_fg_root()); + d->poiDatPath.append("Navaids/poi.dat.gz"); d->carrierDatPath = SGPath(globals->get_fg_root()); d->carrierDatPath.append("Navaids/carrier_nav.dat.gz"); @@ -1134,7 +1123,6 @@ NavDataCache::~NavDataCache() { assert(static_instance == this); static_instance = NULL; - SG_LOG(SG_NAVCACHE, SG_INFO, "closing the navcache"); d.reset(); } @@ -1149,25 +1137,49 @@ NavDataCache* NavDataCache::instance() bool NavDataCache::isRebuildRequired() { - if (isCachedFileModified(d->aptDatPath) || - isCachedFileModified(d->metarDatPath) || - isCachedFileModified(d->navDatPath) || - isCachedFileModified(d->fixDatPath) || - isCachedFileModified(d->airwayDatPath)) + if (d->readOnly) { + return false; + } + + if (flightgear::Options::sharedInstance()->isOptionSet("restore-defaults")) { + SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: restore-defaults requested, will rebuild cache"); + return true; + } + + if (d->isCachedFileModified(d->aptDatPath, true) || + d->isCachedFileModified(d->metarDatPath, true) || + d->isCachedFileModified(d->navDatPath, true) || + d->isCachedFileModified(d->fixDatPath, true) || + d->isCachedFileModified(d->carrierDatPath, true) || +// since POI loading is disabled on Windows, don't check for it +// this caused: https://code.google.com/p/flightgear-bugs/issues/detail?id=1227 +#ifndef SG_WINDOWS + d->isCachedFileModified(d->poiDatPath, true) || +#endif + d->isCachedFileModified(d->airwayDatPath, true)) { SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: main cache rebuild required"); return true; } - string sceneryPaths = simgear::strutils::join(globals->get_fg_scenery(), ";"); - if (readStringProperty("scenery_paths") != sceneryPaths) { - SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: scenery paths changed, main cache rebuild required"); - return true; - } - + dropGroundnetsIfRequired(); + SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: no main cache rebuild required"); return false; } + +bool NavDataCache::dropGroundnetsIfRequired() +{ + string sceneryPaths = simgear::strutils::join(globals->get_fg_scenery(), ";"); + if (readStringProperty("scenery_paths") != sceneryPaths) { + SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: scenery paths changed, dropping ground nets"); + dropAllGroundnets(); + writeStringProperty("scenery_paths", sceneryPaths); + return true; + } + + return false; +} bool NavDataCache::rebuild() { @@ -1189,46 +1201,76 @@ void NavDataCache::doRebuild() try { d->close(); // completely close the sqlite object d->path.remove(); // remove the file on disk - d->init(); // star again from scratch + d->init(); // start again from scratch - Transaction txn(this); - // initialise the root octree node + // initialise the root octree node d->runSQL("INSERT INTO octree (rowid, children) VALUES (1, 0)"); - + SGTimeStamp st; - st.stamp(); - - airportDBLoad(d->aptDatPath); - SG_LOG(SG_NAVCACHE, SG_INFO, "apt.dat load took:" << st.elapsedMSec()); - - metarDataLoad(d->metarDatPath); - stampCacheFile(d->aptDatPath); - stampCacheFile(d->metarDatPath); - - st.stamp(); - loadFixes(d->fixDatPath); - stampCacheFile(d->fixDatPath); - SG_LOG(SG_NAVCACHE, SG_INFO, "fix.dat load took:" << st.elapsedMSec()); - - st.stamp(); - navDBInit(d->navDatPath); - stampCacheFile(d->navDatPath); - SG_LOG(SG_NAVCACHE, SG_INFO, "nav.dat load took:" << st.elapsedMSec()); - - loadCarrierNav(d->carrierDatPath); - stampCacheFile(d->carrierDatPath); - - st.stamp(); - Airway::load(d->airwayDatPath); - stampCacheFile(d->airwayDatPath); - SG_LOG(SG_NAVCACHE, SG_INFO, "awy.dat load took:" << st.elapsedMSec()); - - d->flushDeferredOctreeUpdates(); - - string sceneryPaths = simgear::strutils::join(globals->get_fg_scenery(), ";"); - writeStringProperty("scenery_paths", sceneryPaths); - - txn.commit(); + { + Transaction txn(this); + + st.stamp(); + airportDBLoad(d->aptDatPath); + SG_LOG(SG_NAVCACHE, SG_INFO, "apt.dat load took:" << st.elapsedMSec()); + + metarDataLoad(d->metarDatPath); + stampCacheFile(d->aptDatPath); + stampCacheFile(d->metarDatPath); + + st.stamp(); + loadFixes(d->fixDatPath); + stampCacheFile(d->fixDatPath); + SG_LOG(SG_NAVCACHE, SG_INFO, "fix.dat load took:" << st.elapsedMSec()); + + st.stamp(); + navDBInit(d->navDatPath); + stampCacheFile(d->navDatPath); + SG_LOG(SG_NAVCACHE, SG_INFO, "nav.dat load took:" << st.elapsedMSec()); + + st.stamp(); + txn.commit(); + SG_LOG(SG_NAVCACHE, SG_INFO, "stage 1 commit took:" << st.elapsedMSec()); + } + +#ifdef SG_WINDOWS + SG_LOG(SG_NAVCACHE, SG_ALERT, "SKIPPING POI load on Windows"); +#else + { + Transaction txn(this); + + st.stamp(); + poiDBInit(d->poiDatPath); + stampCacheFile(d->poiDatPath); + SG_LOG(SG_NAVCACHE, SG_INFO, "poi.dat load took:" << st.elapsedMSec()); + + st.stamp(); + txn.commit(); + SG_LOG(SG_NAVCACHE, SG_INFO, "POI commit took:" << st.elapsedMSec()); + } +#endif + + { + Transaction txn(this); + loadCarrierNav(d->carrierDatPath); + stampCacheFile(d->carrierDatPath); + + st.stamp(); + Airway::load(d->airwayDatPath); + stampCacheFile(d->airwayDatPath); + SG_LOG(SG_NAVCACHE, SG_INFO, "awy.dat load took:" << st.elapsedMSec()); + + d->flushDeferredOctreeUpdates(); + + string sceneryPaths = simgear::strutils::join(globals->get_fg_scenery(), ";"); + writeStringProperty("scenery_paths", sceneryPaths); + + st.stamp(); + txn.commit(); + SG_LOG(SG_NAVCACHE, SG_INFO, "final commit took:" << st.elapsedMSec()); + + } + } catch (sg_exception& e) { SG_LOG(SG_NAVCACHE, SG_ALERT, "caught exception rebuilding navCache:" << e.what()); } @@ -1328,39 +1370,7 @@ void NavDataCache::writeStringListProperty(const string& key, const string_list& bool NavDataCache::isCachedFileModified(const SGPath& path) const { - if (!path.exists()) { - throw sg_io_exception("isCachedFileModified: Missing file:" + path.str()); - } - - sqlite_bind_temp_stdstring(d->statCacheCheck, 1, path.str()); - bool isModified = true; - - if (d->execSelect(d->statCacheCheck)) { - time_t modtime = sqlite3_column_int64(d->statCacheCheck, 0); - time_t delta = std::labs(modtime - path.modTime()); - if (delta != 0) - { - SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: rebuild required for " << path << ". Timestamps: " << modtime << " != " << path.modTime()); - if (delta < 30) - { - // File system time stamp has slightly changed. It's unlikely that the user has managed to change a file, start fgfs, - // and then changed file again within x seconds - so it's suspicious... - SG_LOG(SG_NAVCACHE, SG_ALERT, "NavCache: suspicious file timestamp change. Please report this to FlightGear. " - << "Delta: " << delta << ", Timestamps: " << modtime << ", " << path.modTime() << ", path: " << path); - } - } - else - { - SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: no rebuild required for " << path); - } - - isModified = (delta != 0); - } else { - SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: initial build required for " << path); - } - - d->reset(d->statCacheCheck); - return isModified; + return d->isCachedFileModified(path, false); } void NavDataCache::stampCacheFile(const SGPath& path) @@ -1439,7 +1449,7 @@ void NavDataCache::abortTransaction() d->transactionAborted = true; } -FGPositioned* NavDataCache::loadById(PositionedID rowid) +FGPositionedRef NavDataCache::loadById(PositionedID rowid) { if (rowid == 0) { return NULL; @@ -1451,9 +1461,17 @@ FGPositioned* NavDataCache::loadById(PositionedID rowid) return it->second; // cache it } - FGPositioned* pos = d->loadById(rowid); + sqlite3_int64 aptId; + FGPositioned* pos = d->loadById(rowid, aptId); d->cache.insert(it, PositionedCache::value_type(rowid, pos)); - d->cacheMisses++; + d->cacheMisses++; + + // when we loaded an ILS, we must apply per-airport changes + if ((pos->type() == FGPositioned::ILS) && (aptId > 0)) { + FGAirport* apt = FGPositioned::loadById(aptId); + apt->validateILSData(); + } + return pos; } @@ -1552,26 +1570,12 @@ void NavDataCache::setRunwayILS(PositionedID runway, PositionedID ils) sqlite3_bind_int64(d->setRunwayILS, 1, runway); sqlite3_bind_int64(d->setRunwayILS, 2, ils); d->execUpdate(d->setRunwayILS); -} - -void NavDataCache::updateRunwayThreshold(PositionedID runwayID, const SGGeod &aThreshold, - double aHeading, double aDisplacedThreshold, - double aStopway) -{ -// update the runway information - sqlite3_bind_int64(d->updateRunwayThreshold, 1, runwayID); - sqlite3_bind_double(d->updateRunwayThreshold, 2, aHeading); - sqlite3_bind_double(d->updateRunwayThreshold, 3, aDisplacedThreshold); - sqlite3_bind_double(d->updateRunwayThreshold, 4, aStopway); - d->execUpdate(d->updateRunwayThreshold); - -// compute the new runway center, based on the threshold lat/lon and length, - double offsetFt = (0.5 * d->runwayLengthFt(runwayID)); - SGGeod newCenter= SGGeodesy::direct(aThreshold, aHeading, offsetFt * SG_FEET_TO_METER); - newCenter.setElevationM(aThreshold.getElevationM()); - -// now update the positional data - updatePosition(runwayID, newCenter); + + // and the in-memory one + if (d->cache.find(runway) != d->cache.end()) { + FGRunway* instance = (FGRunway*) d->cache[runway].ptr(); + instance->setILS(ils); + } } PositionedID @@ -1592,15 +1596,22 @@ NavDataCache::insertNavaid(FGPositioned::Type ty, const string& ident, sqlite3_bind_int(d->insertNavaid, 3, range); sqlite3_bind_double(d->insertNavaid, 4, multiuse); sqlite3_bind_int64(d->insertNavaid, 5, runway); + sqlite3_bind_int64(d->insertNavaid, 6, 0); return d->execInsert(d->insertNavaid); } -void NavDataCache::updateILS(PositionedID ils, const SGGeod& newPos, double aHdg) +void NavDataCache::setNavaidColocated(PositionedID navaid, PositionedID colocatedDME) { - sqlite3_bind_int64(d->updateILS, 1, ils); - sqlite3_bind_double(d->updateILS, 2, aHdg); - d->execUpdate(d->updateILS); - updatePosition(ils, newPos); + // Update DB entries... + sqlite3_bind_int64(d->setNavaidColocated, 1, navaid); + sqlite3_bind_int64(d->setNavaidColocated, 2, colocatedDME); + d->execUpdate(d->setNavaidColocated); + + // ...and the in-memory copy of the navrecord + if (d->cache.find(navaid) != d->cache.end()) { + FGNavRecord* rec = (FGNavRecord*) d->cache[navaid].get(); + rec->setColocatedDME(colocatedDME); + } } PositionedID NavDataCache::insertCommStation(FGPositioned::Type ty, @@ -1619,11 +1630,19 @@ PositionedID NavDataCache::insertFix(const std::string& ident, const SGGeod& aPo return d->insertPositioned(FGPositioned::FIX, ident, string(), aPos, 0, true); } -PositionedID NavDataCache::createUserWaypoint(const std::string& ident, const SGGeod& aPos) +PositionedID NavDataCache::createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos) { - return d->insertPositioned(FGPositioned::WAYPOINT, ident, string(), aPos, 0, + return d->insertPositioned(ty, ident, string(), aPos, 0, true /* spatial index */); } + +bool NavDataCache::removePOI(FGPositioned::Type ty, const std::string& aIdent) +{ + d->removePositionedWithIdent(ty, aIdent); + // should remove from the live cache too? + + return true; +} void NavDataCache::setAirportMetar(const string& icao, bool hasMetar) { @@ -1632,20 +1651,26 @@ void NavDataCache::setAirportMetar(const string& icao, bool hasMetar) d->execUpdate(d->setAirportMetar); } -FGPositioned::List NavDataCache::findAllWithIdent(const string& s, - FGPositioned::Filter* filter, bool exact) +//------------------------------------------------------------------------------ +FGPositionedList NavDataCache::findAllWithIdent( const string& s, + FGPositioned::Filter* filter, + bool exact ) { return d->findAllByString(s, "ident", filter, exact); } -FGPositioned::List NavDataCache::findAllWithName(const string& s, - FGPositioned::Filter* filter, bool exact) +//------------------------------------------------------------------------------ +FGPositionedList NavDataCache::findAllWithName( const string& s, + FGPositioned::Filter* filter, + bool exact ) { return d->findAllByString(s, "name", filter, exact); } - -FGPositionedRef NavDataCache::findClosestWithIdent(const string& aIdent, - const SGGeod& aPos, FGPositioned::Filter* aFilter) + +//------------------------------------------------------------------------------ +FGPositionedRef NavDataCache::findClosestWithIdent( const string& aIdent, + const SGGeod& aPos, + FGPositioned::Filter* aFilter ) { sqlite_bind_stdstring(d->findClosestWithIdent, 1, aIdent); if (aFilter) { @@ -1731,13 +1756,19 @@ NavDataCache::getOctreeLeafChildren(int64_t octreeNodeId) */ char** NavDataCache::searchAirportNamesAndIdents(const std::string& aFilter) { - string s = "%" + aFilter + "%"; - sqlite_bind_stdstring(d->searchAirports, 1, s); - + sqlite3_stmt_ptr stmt; unsigned int numMatches = 0, numAllocated = 16; - char** result = (char**) malloc(sizeof(char*) * numAllocated); + string searchTerm("%" + aFilter + "%"); + if (aFilter.empty()) { + stmt = d->getAllAirports; + numAllocated = 4096; // start much larger for all airports + } else { + stmt = d->searchAirports; + sqlite_bind_stdstring(stmt, 1, searchTerm); + } - while (d->stepSelect(d->searchAirports)) { + char** result = (char**) malloc(sizeof(char*) * numAllocated); + while (d->stepSelect(stmt)) { if ((numMatches + 1) >= numAllocated) { numAllocated <<= 1; // double in size! // reallocate results array @@ -1755,18 +1786,18 @@ char** NavDataCache::searchAirportNamesAndIdents(const std::string& aFilter) // which gives a grand total of 7 + name-length + icao-length. // note the ident can be three letters (non-ICAO local strip), four // (default ICAO) or more (extended format ICAO) - int nameLength = sqlite3_column_bytes(d->searchAirports, 1); - int icaoLength = sqlite3_column_bytes(d->searchAirports, 0); + int nameLength = sqlite3_column_bytes(stmt, 1); + int icaoLength = sqlite3_column_bytes(stmt, 0); char* entry = (char*) malloc(7 + nameLength + icaoLength); char* dst = entry; *dst++ = ' '; - memcpy(dst, sqlite3_column_text(d->searchAirports, 1), nameLength); + memcpy(dst, sqlite3_column_text(stmt, 1), nameLength); dst += nameLength; *dst++ = ' '; *dst++ = ' '; *dst++ = ' '; *dst++ = '('; - memcpy(dst, sqlite3_column_text(d->searchAirports, 0), icaoLength); + memcpy(dst, sqlite3_column_text(stmt, 0), icaoLength); dst += icaoLength; *dst++ = ')'; *dst++ = 0; @@ -1775,7 +1806,7 @@ char** NavDataCache::searchAirportNamesAndIdents(const std::string& aFilter) } result[numMatches] = NULL; // end of list marker - d->reset(d->searchAirports); + d->reset(stmt); return result; } @@ -1893,7 +1924,7 @@ NavDataCache::findAirportRunway(const std::string& aName) AirportRunwayPair result; sqlite_bind_stdstring(d->findAirportRunway, 1, parts[0]); - sqlite_bind_stdstring(d->findAirportRunway, 2, parts[1]); + sqlite_bind_stdstring(d->findAirportRunway, 2, cleanRunwayNo(parts[1])); if (d->execSelect(d->findAirportRunway)) { result = AirportRunwayPair(sqlite3_column_int64(d->findAirportRunway, 0), @@ -1908,8 +1939,10 @@ NavDataCache::findAirportRunway(const std::string& aName) } PositionedID -NavDataCache::findILS(PositionedID airport, const string& runway, const string& navIdent) +NavDataCache::findILS(PositionedID airport, const string& aRunway, const string& navIdent) { + string runway(cleanRunwayNo(aRunway)); + sqlite_bind_stdstring(d->findILS, 1, navIdent); sqlite3_bind_int64(d->findILS, 2, airport); sqlite_bind_stdstring(d->findILS, 3, runway); @@ -2136,6 +2169,26 @@ void NavDataCache::dropGroundnetFor(PositionedID aAirport) sqlite3_bind_int64(q, 1, aAirport); d->execUpdate(q); } + +void NavDataCache::dropAllGroundnets() +{ + SG_LOG(SG_NAVCACHE, SG_INFO, "dropping ground-net data"); + beginTransaction(); + d->runSQL("DELETE FROM groundnet_edge"); + d->runSQL("DELETE FROM parking"); + d->runSQL("DELETE FROM taxi_node"); + + sqlite3_stmt_ptr q = d->prepare("DELETE FROM positioned WHERE (type=?1 OR type=?2)"); + sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE); + sqlite3_bind_int(q, 2, FGPositioned::PARKING); + d->execUpdate(q); + commitTransaction(); +} + +bool NavDataCache::isReadOnly() const +{ + return d->readOnly; +} ///////////////////////////////////////////////////////////////////////////////////////// // Transaction RAII object