31#define SQLITE_INT64_TYPE int64_t
32#define SQLITE_UINT64_TYPE uint64_t
34 #include "fg_sqlite3.h"
37#if defined(SG_WINDOWS)
38#define WIN32_LEAN_AND_MEAN
46#include <simgear/bucket/newbucket.hxx>
47#include <simgear/debug/logstream.hxx>
48#include <simgear/io/sg_file.hxx>
49#include <simgear/misc/sg_dir.hxx>
50#include <simgear/misc/sg_path.hxx>
51#include <simgear/misc/strutils.hxx>
52#include <simgear/sg_inlines.h>
53#include <simgear/structure/exception.hxx>
54#include <simgear/threads/SGThread.hxx>
79#define SG_NAVCACHE SG_NAVAID
84const int MAX_RETRIES = 10;
86const int CACHE_SIZE_KBYTES= 32 * 1024;
92void sqlite_bind_stdstring(sqlite3_stmt* stmt,
int value,
const std::string& s)
94 sqlite3_bind_text(stmt, value, s.c_str(), s.length(), SQLITE_STATIC);
99void sqlite_bind_temp_stdstring(sqlite3_stmt* stmt,
int value,
const std::string& s)
101 sqlite3_bind_text(stmt, value, s.c_str(), s.length(), SQLITE_TRANSIENT);
104typedef sqlite3_stmt* sqlite3_stmt_ptr;
106void f_distanceCartSqrFunction(sqlite3_context* ctx,
int argc, sqlite3_value* argv[])
112 SGVec3d posA(sqlite3_value_double(argv[0]),
113 sqlite3_value_double(argv[1]),
114 sqlite3_value_double(argv[2]));
116 SGVec3d posB(sqlite3_value_double(argv[3]),
117 sqlite3_value_double(argv[4]),
118 sqlite3_value_double(argv[5]));
119 sqlite3_result_double(ctx, distSqr(posA, posB));
123static string cleanRunwayNo(
const string& aRwyNo)
125 if (aRwyNo[0] ==
'x') {
129 string result(aRwyNo);
131 if ((aRwyNo.size() == 1) || !isdigit(aRwyNo[1])) {
132 result =
"0" + aRwyNo;
136 if (result.size() > 2) {
137 char suffix = toupper(result[2]);
139 result = result.substr(0, 2);
146class AbandonCacheException :
public sg_exception
149 AbandonCacheException() : sg_exception(
"Cache build cancelled", {}, {},
false)
172 _completionPercent(0),
185 std::lock_guard<std::mutex> g(_lock);
194 SG_LOG(
SG_NAVCACHE, SG_INFO,
"cache rebuild took:" << st.elapsedMSec() <<
"msec");
196 std::lock_guard<std::mutex> g(_lock);
204 std::lock_guard<std::mutex> g(_lock);
211 unsigned int perc = 0;
212 std::lock_guard<std::mutex> g(_lock);
213 perc = _completionPercent;
219 std::lock_guard<std::mutex> g(_lock);
221 _completionPercent = percent;
227 unsigned int _completionPercent;
228 mutable std::mutex _lock;
240 const string&
ident,
const SGGeod& pos) :
250int traceCallback(
unsigned int traceCode,
void* ctx,
void*
p,
void* x)
252 if (traceCode == SQLITE_TRACE_STMT) {
253 SG_LOG(
SG_NAVCACHE, SG_WARN,
"step:" <<
p <<
" text=" << (
char*)x);
255 else if (traceCode == SQLITE_TRACE_PROFILE) {
256 int64_t* nanoSecs = (int64_t*)x;
257 SG_LOG(
SG_NAVCACHE, SG_WARN,
"profile:" <<
p <<
" took " << *nanoSecs);
292 throw sg_exception(
"Nav-cache file is not writeable");
295 int openFlags =
readOnly ? SQLITE_OPEN_READONLY :
296 (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
297 std::string pathUtf8 =
path.utf8Str();
298 int result = sqlite3_open_v2(pathUtf8.c_str(), &
db, openFlags, NULL);
299 if (result == SQLITE_MISUSE) {
301 SG_LOG(
SG_NAVCACHE, SG_WARN,
"Failed to open DB at " <<
path <<
": misuse of Sqlite API");
302 throw sg_exception(
"Navcache failed to open: Sqlite API misuse");
303 }
else if (result != SQLITE_OK) {
304 std::string errMsg = sqlite3_errmsg(
db);
305 SG_LOG(
SG_NAVCACHE, SG_WARN,
"Failed to open DB at " <<
path <<
" with error:\n\t" << errMsg);
306 throw sg_exception(
"Navcache failed to open:" + errMsg);
309 if (!
readOnly && (sqlite3_db_readonly(
db,
nullptr) != 0)) {
310 throw sg_exception(
"Nav-cache file opened but is not writeable");
316 sqlite3_stmt_ptr checkTables =
317 prepare(
"SELECT count(*) FROM sqlite_master WHERE name='properties'");
319 sqlite3_create_function(
db,
"distanceCartSqr", 6, SQLITE_ANY, NULL,
320 f_distanceCartSqrFunction, NULL, NULL);
323 bool didCreate =
false;
324 if (!
readOnly && (sqlite3_column_int(checkTables, 0) == 0)) {
325 SG_LOG(
SG_NAVCACHE, SG_INFO,
"will create tables");
341 int schemaVersion =
outer->readIntProperty(
"schema-version");
343 SG_LOG(
SG_NAVCACHE, SG_INFO,
"Navcache schema mismatch, will rebuild");
344 throw sg_exception(
"Navcache schema has changed", {}, {},
false);
350 std::ostringstream q;
351 q <<
"PRAGMA cache_size=-" << CACHE_SIZE_KBYTES <<
";";
358 for (sqlite3_stmt_ptr stmt :
prepared) {
359 sqlite3_finalize(stmt);
367 SG_LOG(
SG_NAVCACHE, SG_INFO,
"running DB integrity check");
371 sqlite3_stmt_ptr stmt =
prepare(
"PRAGMA quick_check(1)");
373 throw sg_exception(
"DB integrity check failed to run");
376 string v = (
char*) sqlite3_column_text(stmt, 0);
378 throw sg_exception(
"DB integrity check returned:" + v);
381 SG_LOG(
SG_NAVCACHE, SG_INFO,
"NavDataCache integrity check took:" << st.elapsedMSec());
394 sqlite3_stmt_ptr stmt;
395 int result = sqlite3_prepare_v2(
db, sql.c_str(), sql.length(), &stmt,
nullptr);
399 while (result == SQLITE_BUSY) {
400 if (retries > MAX_RETRIES) {
405 SGTimeStamp::sleepForMSec(retryMSec);
406 retryMSec = retryMSec << 1;
408 result = sqlite3_prepare_v2(
db, sql.c_str(), sql.length(), &stmt,
nullptr);
411 if (result == SQLITE_OK) {
416 if (result == SQLITE_MISUSE) {
417 errMsg =
"Sqlite API abuse";
420 errMsg = sqlite3_errmsg(
db);
421 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"Sqlite error:" << errMsg <<
" running:\n\t" << sql);
424 throw sg_exception(
"Sqlite error:" + errMsg, sql);
432 }
catch (sg_exception&) {
433 sqlite3_finalize(stmt);
437 sqlite3_finalize(stmt);
451 throw sg_exception(
"Finalising statement that was not prepared");
461 if (sqlite3_reset(stmt) != SQLITE_OK) {
462 string errMsg = sqlite3_errmsg(
db);
463 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"Sqlite error resetting:" << errMsg);
464 throw sg_exception(
"Sqlite error resetting:" + errMsg, sqlite3_sql(stmt));
476 throw AbandonCacheException{};
482 while (retries < MAX_RETRIES) {
483 result = sqlite3_step(stmt);
484 if (result == SQLITE_ROW) {
488 if (result == SQLITE_DONE) {
492 if (result != SQLITE_BUSY) {
496 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"NavCache contention on select, will retry:" << retries);
497 SGTimeStamp::sleepForMSec(retryMSec);
501 if (retries >= MAX_RETRIES) {
502 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"exceeded maximum number of SQLITE_BUSY retries");
507 if (result == SQLITE_MISUSE) {
508 errMsg =
"Sqlite API abuse";
511 errMsg = sqlite3_errmsg(
db);
512 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"Sqlite error:" << errMsg <<
" (" << result
513 <<
") while running:\n\t" << sqlite3_sql(stmt));
516 throw sg_exception(
"Sqlite error:" + errMsg, sqlite3_sql(stmt));
522 SG_LOG(
SG_NAVCACHE, SG_WARN,
"empty SELECT running:\n\t" << sqlite3_sql(stmt));
524 throw sg_exception(
"no results returned for select", sqlite3_sql(stmt));
531 sqlite3_int64 rowid = sqlite3_last_insert_rowid(
db);
545 for (std::string sql : commands) {
557 for (
auto sql : commands) {
575#define POSITIONED_COLS "guid, type, ident, name, airport, lon, lat, elev_m, octree_node"
576#define AND_TYPED "AND type>=?2 AND type <=?3"
579 "(path, stamp, sha) VALUES (?,?, ?)");
583 loadNavaid =
prepare(
"SELECT range_nm, freq, multiuse, runway, colocated FROM navaid WHERE rowid=?");
586 "stopway, reciprocal, ils FROM runway WHERE rowid=?1");
592 "(SELECT rowid FROM positioned WHERE ident=?1 AND type>=?3 AND type <=?4)");
601 "(type, ident, name, airport, lon, lat, elev_m, octree_node, "
602 "cart_x, cart_y, cart_z)"
603 " VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)");
606 "(rowid, type, ident, name, lon, lat, elev_m, "
607 "cart_x, cart_y, cart_z)"
608 " VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)");
611 "cart_x=?6, cart_y=?7, cart_z=?8 WHERE rowid=?1");
612 updateTempPos =
prepare(
"UPDATE temp_positioned SET lon=?2, lat=?3, elev_m=?4, octree_node=?5, "
613 "cart_x=?6, cart_y=?7, cart_z=?8 WHERE rowid=?1");
615 insertAirport =
prepare(
"INSERT INTO airport (rowid, scenery_path, has_metar) VALUES (?, ?, ?)");
616 insertNavaid =
prepare(
"INSERT INTO navaid (rowid, freq, range_nm, multiuse, runway, colocated)"
617 " VALUES (?1, ?2, ?3, ?4, ?5, ?6)");
620 " VALUES (?, ?, ?)");
622 "(rowid, heading, length_ft, width_m, surface, displaced_threshold, stopway, reciprocal)"
623 " VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)");
633 "positioned.rowid=comm.rowid AND freq_khz=?1 "
634 AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)");
637 "positioned.rowid=navaid.rowid "
639 " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)");
642 "positioned.rowid=navaid.rowid AND freq=?1 " AND_TYPED);
645 "positioned.rowid=navaid.rowid AND runway=?1 AND type=?2");
651#ifdef LAZY_OCTREE_UPDATES
665 " ORDER BY (ident LIKE ?1) DESC");
677 "(SELECT rowid FROM positioned WHERE type=?4 AND ident=?1)");
685 findILS =
prepare(
"SELECT nav.rowid FROM positioned AS nav, positioned AS rwy, navaid WHERE "
686 "nav.ident=?1 AND nav.airport=?2 AND rwy.ident=?3 "
687 "AND rwy.rowid = navaid.runway AND navaid.rowid=nav.rowid "
688 "AND (nav.type=?4 OR nav.type=?5)");
701 "VALUES (?1, ?2, ?3, ?4)");
727 const string&
id,
const string&
name,
const SGGeod& pos)
736 std::move(sceneryPath));
752 return new FGTaxiway(rowId,
id, pos, heading, lengthM, widthM, surface, apt);
755 return new FGHelipad(rowId, apt,
id, pos, heading, lengthM, widthM, surface);
757 double displacedThreshold = sqlite3_column_double(
loadRunwayStmt, 4);
762 displacedThreshold, stopway, surface);
764 if (reciprocal > 0) {
778 const string&
id,
const string&
name,
798 const string&
name,
const SGGeod& pos)
812 int rangeNm = sqlite3_column_int(
loadNavaid, 0),
814 double mulituse = sqlite3_column_double(
loadNavaid, 2);
821 (rowId, ty,
id,
name, pos, freq, rangeNm, mulituse, runway)
823 (rowId, ty,
id,
name, pos, freq, rangeNm, mulituse, runway);
836 throw AbandonCacheException{};
838 SGVec3d cartPos(SGVec3d::fromGeod(pos));
850 assert(intersects(octreeLeaf->
bbox(), cartPos));
866 const string&
name,
const SGGeod& pos,
870 throw AbandonCacheException{};
872 SGVec3d cartPos(SGVec3d::fromGeod(pos));
898 if (!exact) query +=
"%";
901 string matchTerm = exact ?
"=?1" :
" LIKE ?1";
902 string sql =
"SELECT guid FROM all_positioned WHERE " + column + matchTerm;
914 sqlite_bind_stdstring(stmt, 1, query);
916 sqlite3_bind_int(stmt, 2, filter->
minType());
917 sqlite3_bind_int(stmt, 3, filter->
maxType());
920 std::vector<PositionedID> guids;
923 guids.push_back(sqlite3_column_int64(stmt, 0));
931 for (
const auto guid: guids) {
933 if (!filter || filter->
pass(pos)) {
934 result.push_back(pos);
945 result.push_back(sqlite3_column_int64(query, 0));
974 sqlite3_bind_int64(stmt, 1, rowid);
1061 sqlite3_int64& aptId)
1064 throw AbandonCacheException{};
1079 SGGeod pos = SGGeod::fromDegM(lon, lat, elev);
1095 return loadRunway(rowid, ty, ident, pos, aptId);
1111 return new FGFix(rowid, ident, pos);
1140 if (!
path.exists()) {
1142 "NavCache: missing file '" +
path.utf8Str() +
"'",
1143 string(
"NavDataCache::NavDataCachePrivate::isCachedFileModified()"));
1147 sgDebugPriority logLevel = verbose ? SG_WARN : SG_DEBUG;
1149 SG_LOG(
SG_NAVCACHE, logLevel,
"NavCache: (re-)build required because '" <<
1150 path.utf8Str() <<
"' is not in the cache");
1156 time_t delta = std::labs(modtime -
path.modTime());
1158 SG_LOG(
SG_NAVCACHE, SG_DEBUG,
"NavCache: modtime matches, no rebuild required for " <<
path);
1163 const std::string shaHash{(
char*)sqlite3_column_text(
statCacheCheck, 1)};
1165 const auto fileHash = f.computeHash();
1166 const auto isModified = (fileHash != shaHash);
1172 SG_LOG(
SG_NAVCACHE, logLevel,
"NavCache: " <<
path <<
" has changed modtime but contents are unchanged, re-setting cahced mod-time");
1196 auto paths =
globals->get_fg_scenery();
1197 const auto fgrootScenery =
globals->get_fg_root() /
"Scenery";
1198 if (fgrootScenery.exists()) {
1199 auto it = std::find(paths.begin(), paths.end(), fgrootScenery);
1200 if (it == paths.end()) {
1201 paths.push_back(fgrootScenery);
1205 for (
const auto&
path: paths) {
1206 if (!
path.isDir()) {
1208 ": given as a scenery path, but is not a directory");
1212 const SGPath datFilesDir =
1215 if (datFilesDir.isDir()) {
1218 const PathList files = simgear::Dir(datFilesDir).children(
1219 simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
1221 for (
const auto& f : files) {
1222 const std::string
name = f.file();
1223 if (simgear::strutils::ends_with(
name,
".dat") ||
1224 simgear::strutils::ends_with(
name,
".dat.gz")) {
1263 datTypeStr +
".dat files", SGPath::pathListSep);
1264 const auto& datFiles = datFilesGroupInfo.
paths;
1265 auto datFilesIt = datFiles.cbegin();
1266 string_list::const_iterator cachedFilesIt = cachedFiles.begin();
1268 sgDebugPriority logLevel = verbose ? SG_WARN : SG_DEBUG;
1270 if (cachedFilesIt == cachedFiles.end() && datFilesIt != datFiles.end()) {
1272 "NavCache: rebuild required for " <<
datTypeStr <<
".dat files "
1273 "(no file in cache, but " << datFiles.size() <<
" such file" <<
1274 (datFiles.size() > 1 ?
"s" :
"") <<
" found in scenery paths)");
1278 while (datFilesIt != datFiles.end()) {
1279 const SGPath&
path = (datFilesIt++)->datPath;
1281 if (!
path.exists()) {
1283 "NavCache: non-existent file '" +
path.utf8Str() +
"'",
1284 string(
"NavDataCache::NavDataCachePrivate::areDatFilesModified()"));
1287 if (cachedFilesIt == cachedFiles.end()) {
1289 "NavCache: rebuild required for " <<
datTypeStr <<
".dat files "
1290 "(less files in cache than in scenery paths)");
1293 string cachedFile = *(cachedFilesIt++);
1294 string fileOnDisk =
path.realpath().utf8Str();
1299 if (cachedFile != fileOnDisk) {
1301 "NavCache: rebuild required because '" << cachedFile <<
1302 "' (in cache) != '" << fileOnDisk <<
"' (on disk)");
1309 if (cachedFilesIt != cachedFiles.end()) {
1311 "NavCache: rebuild required for " <<
datTypeStr <<
".dat files "
1312 "(more files in cache than in scenery paths)");
1317 "NavCache: no rebuild required for " <<
datTypeStr <<
".dat files");
1333 string(
"TACAN_freq")
1337 string(
"Airports/apt.dat.gz"),
1338 string(
"Airports/metar.dat.gz"),
1339 string(
"Navaids/awy.dat.gz"),
1340 string(
"Navaids/nav.dat.gz"),
1341 string(
"Navaids/fix.dat.gz"),
1342 string(
"Navaids/poi.dat.gz"),
1343 string(
"Navaids/carrier.dat.gz"),
1344 string(
"Navaids/TACAN_freq.dat.gz")
1347NavDataCache::NavDataCache()
1349 const int MAX_TRIES = 3;
1352 std::ostringstream os;
1353 string_list versionParts = simgear::strutils::split(VERSION,
".");
1354 if (versionParts.size() < 2) {
1355 os <<
"navdata.cache";
1357 os <<
"navdata_" << versionParts[0] <<
"_" << versionParts[1] <<
".cache";
1361 sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
1363 for (
int t=0; t < MAX_TRIES; ++t) {
1364 SGPath cachePath = homePath / os.str();
1371 }
catch (sg_exception& e) {
1372 SG_LOG(
SG_NAVCACHE, t == 0 ? SG_WARN : SG_ALERT,
"NavCache: init failed:" << e.what()
1373 <<
" (attempt " << t <<
")");
1375 if (t == (MAX_TRIES - 1)) {
1378 "Unable to open navigation cache",
1379 std::string(
"The navigation data cache could not be opened:")
1380 + e.getMessage(), e.getOrigin());
1386 if (cachePath.exists() && !
fgGetBool(
"/sim/fghome-readonly",
false)) {
1387 bool ok = cachePath.remove();
1389 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"NavCache: failed to remove previous cache file");
1391 "Unable to re-create navigation cache",
1392 "Attempting to remove the old cache failed.",
1393 "Location: " + cachePath.utf8Str());
1412 d->abandonCache =
true;
1413 d->rebuilder.reset();
1460 d->metarDatPath = SGPath(
globals->get_fg_root());
1461 d->metarDatPath.append(
"Airports/metar.dat.gz");
1466 d->carrierDatPath = SGPath(
globals->get_fg_root());
1467 d->carrierDatPath.append(
"Navaids/carrier_nav.dat.gz");
1469 d->airwayDatPath = SGPath(
globals->get_fg_root());
1470 d->airwayDatPath.append(
"Navaids/awy.dat.gz");
1476 auto iter = d->datFilesInfo.find(datFileType);
1477 if (iter == d->datFilesInfo.end()) {
1478 throw sg_error(
"NavCache: requesting info about the list of " +
1479 datTypeStr[datFileType] +
" dat files, however this is not "
1480 "implemented yet!");
1482 return iter->second;
1491 const auto environmentOverride = std::getenv(
"FG_NAVCACHE_REBUILD");
1492 bool dontRebuildFlag =
false;
1493 if (environmentOverride) {
1494 if (!strcmp(environmentOverride,
"0")) {
1495 dontRebuildFlag =
true;
1498 if (!strcmp(environmentOverride,
"1")) {
1499 SG_LOG(
SG_NAVCACHE, SG_MANDATORY_INFO,
"NavCache: forcing rebuild becuase FG_NAVCACHE_REBUILD=1");
1505 SG_LOG(
SG_NAVCACHE, SG_INFO,
"NavCache: restore-defaults requested, will rebuild cache");
1510 d->isCachedFileModified(d->metarDatPath,
true) ||
1513 d->isCachedFileModified(d->carrierDatPath,
true) ||
1515 d->isCachedFileModified(d->airwayDatPath,
true))
1517 if (dontRebuildFlag) {
1518 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"NavCache: skipping rebuild because FG_NAVCACHE_REBUILD=0");
1519 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"!! Navigation and airport data will be incorrect !!");
1522 SG_LOG(
SG_NAVCACHE, SG_INFO,
"NavCache: main cache rebuild required");
1527 SG_LOG(
SG_NAVCACHE, SG_INFO,
"NavCache: no main cache rebuild required");
1532#if defined(SG_WINDOWS)
1533static HANDLE static_fgNavCacheRebuildMutex =
nullptr;
1547#if defined(SG_WINDOWS)
1548 if (static_fgNavCacheRebuildMutex ==
nullptr) {
1550 static_fgNavCacheRebuildMutex = CreateMutexA(
nullptr, FALSE,
"org.flightgear.fgfs.rebuild-navcache");
1551 if (static_fgNavCacheRebuildMutex ==
nullptr) {
1552 SG_LOG(SG_IO, SG_ALERT,
"Failed to create NavCache rebuild mutex");
1556 if (GetLastError() == ERROR_ALREADY_EXISTS) {
1561 const int result = WaitForSingleObject(static_fgNavCacheRebuildMutex, 100);
1562 if (result != WAIT_OBJECT_0) {
1563 SG_LOG(SG_IO, SG_ALERT,
"Failed to lock NavCache rebuild mutex:" << GetLastError());
1569 std::string ps = lockPath.utf8Str();
1572 SG_LOG(SG_IO, SG_ALERT,
"Failed to create rebuild lock file (" << lockPath <<
"):" << simgear::strutils::error_string(errno));
1577 if (errno == EWOULDBLOCK) {
1581 SG_LOG(SG_IO, SG_ALERT,
"Failed to lock file (" << lockPath <<
"):" << simgear::strutils::error_string(errno));
1591#if defined(SG_WINDOWS)
1592 ReleaseMutex(static_fgNavCacheRebuildMutex);
1596 SG_LOG(SG_IO, SG_ALERT,
"Failed to unlock rebuild file:" << simgear::strutils::error_string(errno));
1608 if (!d->rebuilder.get()) {
1612 "Multiple copies of FlightGear are trying to initialise the same navigation database. "
1613 "This means something has gone badly wrong: please report this error.");
1616 "Failed to initialise NavCache rebuild protection");
1620 d->rebuilder->start();
1626 d->rebuilder.reset();
1634#if defined(SG_WINDOWS)
1635 if (!static_fgNavCacheRebuildMutex) {
1636 static_fgNavCacheRebuildMutex = OpenMutexA(SYNCHRONIZE, FALSE,
"org.flightgear.fgfs.rebuild-navcache");
1637 if (!static_fgNavCacheRebuildMutex) {
1640 if (GetLastError() == ERROR_FILE_NOT_FOUND) {
1645 "Unable to check if other copies of FlightGear are initializing. "
1646 "Please report this error.");
1651 auto result = WaitForSingleObject(static_fgNavCacheRebuildMutex, 0);
1652 if (result == WAIT_OBJECT_0) {
1655 ReleaseMutex(static_fgNavCacheRebuildMutex);
1656 CloseHandle(static_fgNavCacheRebuildMutex);
1657 static_fgNavCacheRebuildMutex =
nullptr;
1666 std::string ps = lockPath.utf8Str();
1669 if (errno == ENOENT) {
1673 SG_LOG(SG_IO, SG_ALERT,
"Error opening lock file:" << simgear::strutils::error_string(errno));
1679 if (errno == EWOULDBLOCK) {
1683 SG_LOG(SG_IO, SG_ALERT,
"Error querying lock file:" << simgear::strutils::error_string(errno));
1695 if (!d->rebuilder.get()) {
1699 return d->rebuilder->completionPercent();
1704 if (!d->rebuilder.get()) {
1708 d->rebuilder->setProgress(ph, percent);
1711void NavDataCache::loadDatFiles(
1713 std::function<
void(
const SceneryLocation&, std::size_t, std::size_t)> loader)
1720 std::size_t bytesReadSoFar = 0;
1723 for (
const auto& scLoc : sceneryLocations) {
1724 const string path = scLoc.datPath.realpath().utf8Str();
1725 datFiles.push_back(
path);
1726 SG_LOG(SG_GENERAL, SG_INFO,
1727 "Loading " + typeStr +
".dat file: '" << std::move(
path) <<
"'");
1728 loader(scLoc, bytesReadSoFar, datFilesInfo.
totalSize);
1729 bytesReadSoFar += scLoc.datPath.sizeInBytes();
1735 SGPath::pathListSep);
1737 typeStr +
".dat files load took: " <<
1741void NavDataCache::doRebuild()
1743 rebuildInProgress =
true;
1751 d->runSQL(
"INSERT INTO octree (rowid, children) VALUES (1, 0)");
1756 APTLoader aptLoader;
1757 FixesLoader fixesLoader;
1758 NavLoader navLoader;
1760 using namespace std::placeholders;
1767 SG_LOG(
SG_NAVCACHE, SG_DEBUG,
"Processing airports");
1768 aptLoader.loadAirports();
1770 "processing airports took:" <<
1785 SG_LOG(
SG_NAVCACHE, SG_INFO,
"stage 1 commit took:" << st.elapsedMSec());
1790 POILoader poisLoader;
1792 using namespace std::placeholders;
1798 SG_LOG(
SG_NAVCACHE, SG_INFO,
"poi.dat load took:" << st.elapsedMSec());
1803 SG_LOG(
SG_NAVCACHE, SG_INFO,
"POI commit took:" << st.elapsedMSec());
1808 NavLoader navLoader;
1809 navLoader.loadCarrierNav(d->carrierDatPath);
1815 SG_LOG(
SG_NAVCACHE, SG_INFO,
"awy.dat load took:" << st.elapsedMSec());
1817 d->flushDeferredOctreeUpdates();
1824 SG_LOG(
SG_NAVCACHE, SG_INFO,
"final commit took:" << st.elapsedMSec());
1828 }
catch (sg_exception& e) {
1829 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"caught exception rebuilding navCache:" << e.what());
1832 rebuildInProgress =
false;
1837 sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
1840 if (d->execSelect(d->readPropertyQuery)) {
1841 result = sqlite3_column_int(d->readPropertyQuery, 0);
1843 SG_LOG(
SG_NAVCACHE, SG_WARN,
"readIntProperty: unknown:" << key);
1846 d->reset(d->readPropertyQuery);
1852 sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
1853 double result = 0.0;
1854 if (d->execSelect(d->readPropertyQuery)) {
1855 result = sqlite3_column_double(d->readPropertyQuery, 0);
1857 SG_LOG(
SG_NAVCACHE, SG_WARN,
"readDoubleProperty: unknown:" << key);
1860 d->reset(d->readPropertyQuery);
1866 sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
1868 if (d->execSelect(d->readPropertyQuery)) {
1869 result = (
char*) sqlite3_column_text(d->readPropertyQuery, 0);
1871 SG_LOG(
SG_NAVCACHE, SG_WARN,
"readStringProperty: unknown:" << key);
1874 d->reset(d->readPropertyQuery);
1880 d->writeIntProperty(key, value);
1886 sqlite_bind_stdstring(d->clearProperty, 1, key);
1887 d->execUpdate(d->clearProperty);
1889 sqlite_bind_stdstring(d->writePropertyQuery, 1, key);
1890 sqlite_bind_stdstring(d->writePropertyQuery, 2, value);
1891 d->execUpdate(d->writePropertyQuery);
1898 sqlite_bind_stdstring(d->clearProperty, 1, key);
1899 d->execUpdate(d->clearProperty);
1901 sqlite_bind_stdstring(d->writePropertyQuery, 1, key);
1902 sqlite3_bind_double(d->writePropertyQuery, 2, value);
1903 d->execUpdate(d->writePropertyQuery);
1909 sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
1911 while (d->stepSelect(d->readPropertyQuery)) {
1912 result.push_back((
char*) sqlite3_column_text(d->readPropertyQuery, 0));
1914 d->reset(d->readPropertyQuery);
1922 sqlite_bind_stdstring(d->clearProperty, 1, key);
1923 d->execUpdate(d->clearProperty);
1925 for (
string value : values) {
1926 sqlite_bind_stdstring(d->writePropertyMulti, 1, key);
1927 sqlite_bind_stdstring(d->writePropertyMulti, 2, value);
1928 d->execInsert(d->writePropertyMulti);
1934 const char* separator)
1946 const char* separator)
1958 if (!values.empty()) {
1959 s = simgear::strutils::join(values, separator) + string(separator);
1967 return d->isCachedFileModified(
path,
false);
1973 sqlite_bind_temp_stdstring(d->stampFileCache, 1,
path.realpath().utf8Str());
1974 sqlite3_bind_int64(d->stampFileCache, 2,
path.modTime());
1978 sqlite_bind_temp_stdstring(d->stampFileCache, 3, f.computeHash());
1980 sqlite_bind_temp_stdstring(d->stampFileCache, 3, sha);
1982 d->execInsert(d->stampFileCache);
1986void NavDataCache::beginTransaction()
1988 if (d->transactionLevel == 0) {
1989 d->transactionAborted =
false;
1990 d->stepSelect(d->beginTransactionStmt);
1991 sqlite3_reset(d->beginTransactionStmt);
1994 ++d->transactionLevel;
1997void NavDataCache::commitTransaction()
1999 assert(d->transactionLevel > 0);
2000 if (--d->transactionLevel == 0) {
2004 sqlite3_stmt_ptr q = d->transactionAborted ? d->rollbackTransactionStmt : d->commitTransactionStmt;
2008 while (retries < MAX_RETRIES) {
2009 result = sqlite3_step(q);
2010 if (result == SQLITE_DONE) {
2017 if (sqlite3_get_autocommit(d->db)) {
2018 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"commit: was rolled back!" << retries);
2020 d->transactionAborted =
true;
2024 if (result != SQLITE_BUSY) {
2028 SGTimeStamp::sleepForMSec(++retries * 100);
2029 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"NavCache contention on commit, will retry:" << retries);
2033 if (result != SQLITE_DONE) {
2034 errMsg = sqlite3_errmsg(d->db);
2036 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"Sqlite error:" << errMsg <<
" for " << result
2037 <<
" while running:\n\t" << sqlite3_sql(q));
2044void NavDataCache::abortTransaction()
2046 SG_LOG(
SG_NAVCACHE, SG_WARN,
"NavCache: aborting transaction");
2049 assert(d->transactionLevel > 0);
2050 if (--d->transactionLevel == 0) {
2051 d->stepSelect(d->rollbackTransactionStmt);
2052 sqlite3_reset(d->rollbackTransactionStmt);
2055 d->transactionAborted =
true;
2060 std::for_each(d->cache.begin(), d->cache.end(), [](PositionedCache::value_type& v) {
2061 if (v.second->type() == FGPositioned::MOBILE_TACAN) {
2062 auto mobile = fgpositioned_cast<FGMobileNavRecord>(v.second);
2063 mobile->clearVehicle();
2070 if (!d || (rowid == 0)) {
2074 PositionedCache::iterator it = d->cache.find(rowid);
2075 if (it != d->cache.end()) {
2080 sqlite3_int64 aptId;
2082 if (rebuildInProgress) {
2088 d->cache.insert(it, PositionedCache::value_type(rowid, pos));
2102 const SGPath& sceneryPath)
2104 const string sceneryPath_str = sceneryPath.utf8Str();
2108 sqlite3_int64 rowId = d->insertPositioned(ty, ident,
name, SGGeod(),
2112 sqlite3_bind_int64(d->insertAirport, 1, rowId);
2113 sqlite_bind_stdstring(d->insertAirport, 2, sceneryPath_str);
2114 d->execInsert(d->insertAirport);
2121 const bool isTemporary = (item < 0);
2122 SGVec3d cartPos(SGVec3d::fromGeod(pos));
2123 auto it = d->cache.find(item);
2130 assert(it != d->cache.end());
2132 SGVec3d oldCartPos(SGVec3d::fromGeod(it->second->geod()));
2133 const auto ty = it->second->type();
2139 if (oldOctreeLeaf != newOctreeLeaf) {
2144 it->second->modifyPosition(pos);
2148 if (it != d->cache.end()) {
2149 d->cache[item]->modifyPosition(pos);
2152 auto stmt = isTemporary ? d->updateTempPos : d->updatePosition;
2153 sqlite3_bind_int(stmt, 1, item);
2154 sqlite3_bind_double(stmt, 2, pos.getLongitudeDeg());
2155 sqlite3_bind_double(stmt, 3, pos.getLatitudeDeg());
2156 sqlite3_bind_double(stmt, 4, pos.getElevationM());
2168 sqlite3_bind_int64(stmt, 5, octreeLeaf->
guid());
2171 sqlite3_bind_double(stmt, 6, cartPos.x());
2172 sqlite3_bind_double(stmt, 7, cartPos.y());
2173 sqlite3_bind_double(stmt, 8, cartPos.z());
2176 d->execUpdate(stmt);
2182 pos, airportId,
true );
2188 double heading,
double length,
double width,
double displacedThreshold,
2189 double stopway,
int markings,
int approach,
int tdz,
int reil,
2190 int surfaceCode,
int shoulder_code,
float smoothness,
int center_lights,
2191 int edge_lights,
int distance_remaining)
2197 sqlite3_int64 rowId = d->insertPositioned(ty, cleanRunwayNo(ident),
"", pos, apt,
2199 sqlite3_bind_int64(d->insertRunway, 1, rowId);
2200 sqlite3_bind_double(d->insertRunway, 2, heading);
2201 sqlite3_bind_double(d->insertRunway, 3, length);
2202 sqlite3_bind_double(d->insertRunway, 4, width);
2203 sqlite3_bind_int(d->insertRunway, 5, surfaceCode);
2204 sqlite3_bind_double(d->insertRunway, 6, displacedThreshold);
2205 sqlite3_bind_double(d->insertRunway, 7, stopway);
2206 sqlite3_bind_int(d->insertRunway, 8, markings);
2207 sqlite3_bind_int(d->insertRunway, 9, approach);
2208 sqlite3_bind_int(d->insertRunway, 10, tdz);
2209 sqlite3_bind_int(d->insertRunway, 11, reil);
2210 sqlite3_bind_int(d->insertRunway, 12, shoulder_code);
2211 sqlite3_bind_double(d->insertRunway, 13, smoothness);
2212 sqlite3_bind_int(d->insertRunway, 14, center_lights);
2213 sqlite3_bind_int(d->insertRunway, 15, edge_lights);
2214 sqlite3_bind_int(d->insertRunway, 16, distance_remaining);
2216 return d->execInsert(d->insertRunway);
2222 double heading,
double length,
double width,
double displacedThreshold,
2223 double stopway,
int surfaceCode)
2226 heading, length, width, displacedThreshold,
2227 stopway, 0, 0, 0, 0,
2228 surfaceCode, 0, 0.0f, 0, 0, 0);
2233 sqlite3_bind_int64(d->setRunwayReciprocal, 1, runway);
2234 sqlite3_bind_int64(d->setRunwayReciprocal, 2, recip);
2235 d->execUpdate(d->setRunwayReciprocal);
2238 sqlite3_bind_int64(d->setRunwayReciprocal, 2, runway);
2239 sqlite3_bind_int64(d->setRunwayReciprocal, 1, recip);
2240 d->execUpdate(d->setRunwayReciprocal);
2245 sqlite3_bind_int64(d->setRunwayILS, 1, runway);
2246 sqlite3_bind_int64(d->setRunwayILS, 2, ils);
2247 d->execUpdate(d->setRunwayILS);
2250 if (d->cache.find(runway) != d->cache.end()) {
2258 const string&
name,
const SGGeod& pos,
2259 int freq,
int range,
double multiuse,
2262 bool spatialIndex =
true;
2264 spatialIndex =
false;
2267 sqlite3_int64 rowId = d->insertPositioned(ty, ident,
name, pos, apt,
2269 sqlite3_bind_int64(d->insertNavaid, 1, rowId);
2270 sqlite3_bind_int(d->insertNavaid, 2, freq);
2271 sqlite3_bind_int(d->insertNavaid, 3, range);
2272 sqlite3_bind_double(d->insertNavaid, 4, multiuse);
2273 sqlite3_bind_int64(d->insertNavaid, 5, runway);
2274 sqlite3_bind_int64(d->insertNavaid, 6, 0);
2275 return d->execInsert(d->insertNavaid);
2281 sqlite3_bind_int64(d->setNavaidColocated, 1, navaid);
2282 sqlite3_bind_int64(d->setNavaidColocated, 2, colocatedDME);
2283 d->execUpdate(d->setNavaidColocated);
2286 if (d->cache.find(navaid) != d->cache.end()) {
2288 rec->setColocatedDME(colocatedDME);
2293 const string&
name,
const SGGeod& pos,
int freq,
int range,
2296 sqlite3_int64 rowId = d->insertPositioned(ty,
"",
name, pos, apt,
true);
2297 sqlite3_bind_int64(d->insertCommStation, 1, rowId);
2298 sqlite3_bind_int(d->insertCommStation, 2, freq);
2299 sqlite3_bind_int(d->insertCommStation, 3, range);
2300 return d->execInsert(d->insertCommStation);
2304 const std::string&
name,
bool isTransient)
2310 return d->insertPositioned(ty, ident,
name, aPos, 0,
2318 const bool isTemporary = ref->guid() < 0;
2325 d->removePositioned(ref->guid());
2327 auto it = d->cache.find(ref->guid());
2335 sqlite_bind_stdstring(d->setAirportMetar, 1, icao);
2336 sqlite3_bind_int(d->setAirportMetar, 2, hasMetar);
2337 d->execUpdate(d->setAirportMetar);
2345 return d->findAllByString(s,
"ident", filter, exact);
2353 return d->findAllByString(s,
"name", filter, exact);
2361 sqlite_bind_stdstring(d->findClosestWithIdent, 1, aIdent);
2363 sqlite3_bind_int(d->findClosestWithIdent, 2, aFilter->
minType());
2364 sqlite3_bind_int(d->findClosestWithIdent, 3, aFilter->
maxType());
2370 SGVec3d cartPos(SGVec3d::fromGeod(aPos));
2371 sqlite3_bind_double(d->findClosestWithIdent, 4, cartPos.x());
2372 sqlite3_bind_double(d->findClosestWithIdent, 5, cartPos.y());
2373 sqlite3_bind_double(d->findClosestWithIdent, 6, cartPos.z());
2377 while (d->stepSelect(d->findClosestWithIdent)) {
2379 if (aFilter && !aFilter->
pass(pos)) {
2387 d->reset(d->findClosestWithIdent);
2394 sqlite3_bind_int64(d->getOctreeChildren, 1, octreeNodeId);
2395 if (!d->execSelect(d->getOctreeChildren)) {
2403 int children = sqlite3_column_int(d->getOctreeChildren, 0);
2404 d->reset(d->getOctreeChildren);
2414 sqlite3_bind_int64(d->insertOctree, 1, nd->
guid());
2415 d->execInsert(d->insertOctree);
2417#ifdef LAZY_OCTREE_UPDATES
2418 d->deferredOctreeUpdates.insert(pr);
2421 int childIndex = nd->
guid() & 0x07;
2423 sqlite3_bind_int64(d->updateOctreeChildren, 1, pr->
guid());
2425 int childMask = 1 << childIndex;
2426 sqlite3_bind_int(d->updateOctreeChildren, 2, childMask);
2427 d->execUpdate(d->updateOctreeChildren);
2434 sqlite3_bind_int64(d->getOctreeLeafChildren, 1, octreeNodeId);
2437 while (d->stepSelect(d->getOctreeLeafChildren)) {
2439 (sqlite3_column_int(d->getOctreeLeafChildren, 1));
2440 r.push_back(std::make_pair(ty,
2441 sqlite3_column_int64(d->getOctreeLeafChildren, 0)));
2444 d->reset(d->getOctreeLeafChildren);
2456 sqlite3_stmt_ptr stmt;
2457 unsigned int numMatches = 0, numAllocated = 16;
2458 string heliport(
"HELIPORT");
2459 bool heli_p = searchInput.substr(0, heliport.length()) == heliport;
2460 auto pos = searchInput.find(
":");
2461 string aFilter((pos != string::npos) ? searchInput.substr(pos+1) : searchInput);
2462 string searchTerm(
"%" + aFilter +
"%");
2464 if (aFilter.empty() && !heli_p) {
2465 stmt = d->getAllAirports;
2466 numAllocated = 4096;
2468 stmt = d->searchAirports;
2469 sqlite_bind_stdstring(stmt, 1, searchTerm);
2480 char** result = (
char**) malloc(
sizeof(
char*) * numAllocated);
2481 while (d->stepSelect(stmt)) {
2482 if ((numMatches + 1) >= numAllocated) {
2485 char** nresult = (
char**) malloc(
sizeof(
char*) * numAllocated);
2486 memcpy(nresult, result,
sizeof(
char*) * numMatches);
2499 int nameLength = sqlite3_column_bytes(stmt, 1);
2500 int icaoLength = sqlite3_column_bytes(stmt, 0);
2501 char* entry = (
char*) malloc(7 + nameLength + icaoLength);
2504 memcpy(dst, sqlite3_column_text(stmt, 1), nameLength);
2510 memcpy(dst, sqlite3_column_text(stmt, 0), icaoLength);
2515 result[numMatches++] = entry;
2518 result[numMatches] = NULL;
2527 if (matches.empty()) {
2539 sqlite3_bind_int(d->findCommByFreq, 1, freqKhz);
2541 sqlite3_bind_int(d->findCommByFreq, 2, aFilter->
minType());
2542 sqlite3_bind_int(d->findCommByFreq, 3, aFilter->
maxType());
2548 SGVec3d cartPos(SGVec3d::fromGeod(aPos));
2549 sqlite3_bind_double(d->findCommByFreq, 4, cartPos.x());
2550 sqlite3_bind_double(d->findCommByFreq, 5, cartPos.y());
2551 sqlite3_bind_double(d->findCommByFreq, 6, cartPos.z());
2554 while (d->execSelect(d->findCommByFreq)) {
2555 const auto id = sqlite3_column_int64(d->findCommByFreq, 0);
2558 if (aFilter && !aFilter->
pass(
p)) {
2564 matches.push_back(
id);
2567 d->reset(d->findCommByFreq);
2574 sqlite3_bind_int(d->findNavsByFreq, 1, freqKhz);
2576 sqlite3_bind_int(d->findNavsByFreq, 2, aFilter->
minType());
2577 sqlite3_bind_int(d->findNavsByFreq, 3, aFilter->
maxType());
2583 SGVec3d cartPos(SGVec3d::fromGeod(aPos));
2584 sqlite3_bind_double(d->findNavsByFreq, 4, cartPos.x());
2585 sqlite3_bind_double(d->findNavsByFreq, 5, cartPos.y());
2586 sqlite3_bind_double(d->findNavsByFreq, 6, cartPos.z());
2588 return d->selectIds(d->findNavsByFreq);
2594 sqlite3_bind_int(d->findNavsByFreqNoPos, 1, freqKhz);
2596 sqlite3_bind_int(d->findNavsByFreqNoPos, 2, aFilter->
minType());
2597 sqlite3_bind_int(d->findNavsByFreqNoPos, 3, aFilter->
maxType());
2603 return d->selectIds(d->findNavsByFreqNoPos);
2614 sqlite3_bind_int64(d->getAirportItems, 1, apt);
2615 sqlite3_bind_int(d->getAirportItems, 2, ty);
2616 sqlite3_bind_int(d->getAirportItems, 3, maxTy);
2618 return d->selectIds(d->getAirportItems);
2623 const std::string& ident)
2625 sqlite3_bind_int64(d->getAirportItemByIdent, 1, apt);
2626 sqlite_bind_stdstring(d->getAirportItemByIdent, 2, ident);
2627 sqlite3_bind_int(d->getAirportItemByIdent, 3, ty);
2630 if (d->execSelect(d->getAirportItemByIdent)) {
2631 result = sqlite3_column_int64(d->getAirportItemByIdent, 0);
2634 d->reset(d->getAirportItemByIdent);
2641 if (aName.empty()) {
2645 string_list parts = simgear::strutils::split(aName);
2646 if (parts.size() < 2) {
2647 SG_LOG(
SG_NAVCACHE, SG_WARN,
"findAirportRunway: malformed name:" << aName);
2652 sqlite_bind_stdstring(d->findAirportRunway, 1, parts[0]);
2653 const auto cleanedRunway = cleanRunwayNo(parts[1]);
2654 sqlite_bind_stdstring(d->findAirportRunway, 2, cleanedRunway);
2656 if (d->execSelect(d->findAirportRunway)) {
2658 sqlite3_column_int64(d->findAirportRunway, 1));
2661 SG_LOG(
SG_NAVCACHE, SG_WARN,
"findAirportRunway: unknown airport/runway:" << aName);
2664 d->reset(d->findAirportRunway);
2671 string runway(cleanRunwayNo(aRunway));
2673 sqlite_bind_stdstring(d->findILS, 1, navIdent);
2674 sqlite3_bind_int64(d->findILS, 2, airport);
2675 sqlite_bind_stdstring(d->findILS, 3, runway);
2677 if (d->execSelect(d->findILS)) {
2678 result = sqlite3_column_int64(d->findILS, 0);
2681 d->reset(d->findILS);
2687 assert((network == 1) || (network == 2));
2689 sqlite3_bind_int(d->findAirwayNet, 1, network);
2690 sqlite_bind_stdstring(d->findAirwayNet, 2, aName);
2693 if (d->execSelect(d->findAirwayNet)) {
2695 airway = sqlite3_column_int(d->findAirwayNet, 0);
2696 }
else if (create) {
2697 d->reset(d->insertAirway);
2698 sqlite_bind_stdstring(d->insertAirway, 1, aName);
2699 sqlite3_bind_int(d->insertAirway, 2, network);
2700 airway = d->execInsert(d->insertAirway);
2705 d->reset(d->findAirwayNet);
2711 sqlite_bind_stdstring(d->findAirway, 1, aName);
2714 if (d->execSelect(d->findAirway)) {
2716 airway = sqlite3_column_int(d->findAirway, 0);
2719 d->reset(d->findAirway);
2726 sqlite3_bind_int(d->insertAirwayEdge, 1, network);
2727 sqlite3_bind_int(d->insertAirwayEdge, 2, airwayID);
2728 sqlite3_bind_int64(d->insertAirwayEdge, 3, from);
2729 sqlite3_bind_int64(d->insertAirwayEdge, 4, to);
2730 d->execInsert(d->insertAirwayEdge);
2735 sqlite3_bind_int(d->isPosInAirway, 1, network);
2736 sqlite3_bind_int64(d->isPosInAirway, 2, pos);
2737 bool ok = d->execSelect(d->isPosInAirway);
2738 d->reset(d->isPosInAirway);
2745 sqlite3_bind_int(d->airwayEdgesFrom, 1, network);
2746 sqlite3_bind_int64(d->airwayEdgesFrom, 2, pos);
2749 while (d->stepSelect(d->airwayEdgesFrom)) {
2751 sqlite3_column_int(d->airwayEdgesFrom, 0),
2752 sqlite3_column_int64(d->airwayEdgesFrom, 1)
2756 d->reset(d->airwayEdgesFrom);
2760 sqlite3_bind_int(d->airwayEdgesTo, 1, network);
2761 sqlite3_bind_int64(d->airwayEdgesTo, 2, pos);
2763 while (d->stepSelect(d->airwayEdgesTo)) {
2765 sqlite3_column_int(d->airwayEdgesTo, 0),
2766 sqlite3_column_int64(d->airwayEdgesTo, 1)
2770 d->reset(d->airwayEdgesTo);
2777 sqlite3_bind_int(d->loadAirway, 1, airwayID);
2778 bool ok = d->execSelect(d->loadAirway);
2781 string ident = (
char*) sqlite3_column_text(d->loadAirway, 0);
2783 result =
new Airway(ident, network, airwayID, 0, 0);
2785 d->reset(d->loadAirway);
2791 d->reset(d->airwayEdges);
2792 sqlite3_bind_int(d->airwayEdges, 1,
id);
2794 typedef std::pair<PositionedID, PositionedID> Edge;
2795 typedef std::deque<Edge> EdgeVec;
2796 typedef std::deque<PositionedID> PositionedIDDeque;
2800 while (d->stepSelect(d->airwayEdges)) {
2801 rawEdges.push_back(Edge(sqlite3_column_int64(d->airwayEdges, 0),
2802 sqlite3_column_int64(d->airwayEdges, 1)
2806 d->reset(d->airwayEdges);
2807 if (rawEdges.empty()) {
2814 while (!rawEdges.empty()) {
2815 bool didAddEdge =
false;
2816 std::set<PositionedID> seen;
2818 PositionedIDDeque linearAirway;
2820 lastId = rawEdges.front().second;
2823 linearAirway.push_back(firstId);
2824 linearAirway.push_back(lastId);
2825 seen.insert(firstId);
2826 seen.insert(lastId);
2827 rawEdges.pop_front();
2829 while (!rawEdges.empty()) {
2830 Edge e = rawEdges.front();
2831 rawEdges.pop_front();
2833 bool seenFirst = (seen.find(e.first) != seen.end());
2834 bool seenSecond = (seen.find(e.second) != seen.end());
2837 assert(!(seenFirst && seenSecond));
2839 if (!seenFirst && !seenSecond) {
2841 nextDeque.push_back(e);
2842 if (rawEdges.empty()) {
2843 rawEdges = nextDeque;
2859 if (seenFirst && (e.first == firstId)) {
2860 linearAirway.push_front(e.second);
2862 seen.insert(e.second);
2863 }
else if (seenSecond && (e.second == firstId)) {
2864 linearAirway.push_front(e.first);
2866 seen.insert(e.first);
2867 }
else if (seenFirst && (e.first == lastId)) {
2868 linearAirway.push_back(e.second);
2870 seen.insert(e.second);
2871 }
else if (seenSecond && (e.second == lastId)) {
2872 linearAirway.push_back(e.first);
2874 seen.insert(e.first);
2878 if (rawEdges.empty()) {
2879 rawEdges = nextDeque;
2884 if (!result.empty())
2885 result.push_back(0);
2886 result.insert(result.end(), linearAirway.begin(), linearAirway.end());
2889 SG_LOG(SG_AUTOPILOT, SG_WARN,
"Airway:" <<
id);
2890 for (
unsigned int i=0;
i<result.size(); ++
i) {
2891 if (result.at(
i) == 0) {
2892 SG_LOG(SG_AUTOPILOT, SG_WARN,
i <<
" <break>");
2894 SG_LOG(SG_AUTOPILOT, SG_WARN,
i <<
" " <<
loadById(result.at(
i))->ident());
2904 sqlite3_bind_int64(d->findNavaidForRunway, 1, runway);
2905 sqlite3_bind_int(d->findNavaidForRunway, 2, ty);
2908 if (d->execSelect(d->findNavaidForRunway)) {
2909 result = sqlite3_column_int64(d->findNavaidForRunway, 0);
2912 d->reset(d->findNavaidForRunway);
2928 return d->nextTransientId--;
2940 _instance->beginTransaction();
2946 if (_instance->d->abandonCache || _instance->isReadOnly()) {
2951 SG_LOG(
SG_NAVCACHE, SG_INFO,
"aborting cache transaction!");
2952 _instance->abortTransaction();
2958 if (_instance->isReadOnly()) {
2962 assert(!_committed);
2964 _instance->commitTransaction();
2981 int err = sqlite3_step(
query);
2982 if (err == SQLITE_DONE) {
2984 }
else if (err == SQLITE_ROW) {
2986 std::lock_guard<std::mutex> g(
lock);
2988 }
else if (err == SQLITE_BUSY) {
2990 SGTimeStamp::sleepForMSec(1);
2992 std::string errMsg = sqlite3_errmsg(
db);
2993 SG_LOG(
SG_NAVCACHE, SG_ALERT,
"Sqlite error:" << errMsg <<
" running threaded search query");
2997 std::lock_guard<std::mutex> g(
lock);
3013 int openFlags = SQLITE_OPEN_READONLY;
3014 std::string pathUtf8 =
p.utf8Str();
3015 sqlite3_open_v2(pathUtf8.c_str(), &d->db, openFlags, NULL);
3019 sql =
"SELECT rowid FROM positioned WHERE name LIKE '%" + term
3020 +
"%' AND (type >= 1 AND type <= 3)";
3025 sql =
"SELECT rowid FROM positioned WHERE name LIKE '%" + term
3026 +
"%' AND ((type >= 1 AND type <= 3) OR ((type >= 9 AND type <= 11)) OR (type=18 AND name LIKE '% TACAN') ) ";
3028 sqlite3_prepare_v2(d->db, sql.c_str(), sql.length(), &d->query, NULL);
3036 std::lock_guard<std::mutex> g(d->lock);
3041 sqlite3_finalize(d->query);
3042 sqlite3_close_v2(d->db);
3049 std::lock_guard<std::mutex> g(d->lock);
3050 r = std::move(d->results);
3057 std::lock_guard<std::mutex> g(d->lock);
3058 return d->isComplete;
#define TEMPORARY_SCHEMA_SQL
void validateILSData()
reload the ILS data from XML if required.
static void clearAirportsCache()
const PathList & get_fg_scenery() const
const SGPath & get_fg_home() const
void setColocatedDME(PositionedID other)
Predicate class to support custom filtering of FGPositioned queries Default implementation of this pa...
virtual Type maxType() const
virtual Type minType() const
virtual bool pass(FGPositioned *aPos) const
Over-rideable filter method.
FGPositioned(PositionedID aGuid, Type ty, const std::string &aIdent, const SGGeod &aPos)
PositionedID guid() const
static SGSharedPtr< T > loadById(PositionedID id)
@ TOWER
an actual airport tower - not a radio comms facility!
@ DME
important that DME & TACAN are adjacent to keep the TacanFilter efficient - DMEs are proxies for TACA...
const std::string & ident() const
void setReciprocalRunway(PositionedID other)
void setILS(PositionedID nav)
void readAptDatFile(const NavDataCache::SceneryLocation &sceneryLocation, std::size_t bytesReadSoFar, std::size_t totalSizeOfAllAptDatFiles)
AirportTower(PositionedID &guid, PositionedID airport, const string &ident, const SGGeod &pos)
static void loadAWYDat(const SGPath &path)
void setAirport(PositionedID apt)
void loadFixes(const NavDataCache::SceneryLocation &sceneryLocation, std::size_t bytesReadSoFar, std::size_t totalSizeOfAllDatFiles)
sqlite3_stmt_ptr setNavaidColocated
FGPositionedList findAllByString(const string &s, const string &column, FGPositioned::Filter *filter, bool exact)
sqlite3_stmt_ptr getAirportItemByIdent
sqlite3_stmt_ptr findAirportRunway
void writeIntProperty(const string &key, int value)
sqlite3_stmt_ptr loadAirportStmt
std::vector< sqlite3_stmt_ptr > StmtVec
sqlite3_stmt_ptr findAirway
PositionedIDVec selectIds(sqlite3_stmt_ptr query)
sqlite3_stmt_ptr commitTransactionStmt
sqlite3_stmt_ptr getAirportItems
sqlite3_stmt_ptr insertAirwayEdge
std::map< string, sqlite3_stmt_ptr > findByStringDict
void initTemporaryTables()
sqlite3_stmt_ptr insertOctree
sqlite3_stmt_ptr updateOctreeChildren
FGRunwayBase * loadRunway(sqlite3_int64 rowId, FGPositioned::Type ty, const string &id, const SGGeod &pos, PositionedID apt)
FGPositioned * loadNav(sqlite3_int64 rowId, FGPositioned::Type ty, const string &id, const string &name, const SGGeod &pos)
sqlite3_stmt_ptr removeTempPosQuery
sqlite3_stmt_ptr getOctreeChildren
sqlite3_stmt_ptr findCommByFreq
sqlite3_stmt_ptr airwayEdges
NavDataCachePrivate(const SGPath &p, NavDataCache *o)
sqlite3_stmt_ptr readPropertyQuery
sqlite3_stmt_ptr loadPositioned
sqlite3_stmt_ptr findNavsByFreqNoPos
bool execSelect(sqlite3_stmt_ptr stmt)
sqlite3_stmt_ptr getOctreeLeafChildren
sqlite3_stmt_ptr updatePosition
PositionedID insertTemporaryPositioned(PositionedID guid, FGPositioned::Type ty, const string &ident, const string &name, const SGGeod &pos, bool spatialIndex)
sqlite3_stmt_ptr airwayEdgesTo
void flushDeferredOctreeUpdates()
sqlite3_stmt_ptr beginTransactionStmt
sqlite3_stmt_ptr insertTower
void execUpdate(sqlite3_stmt_ptr stmt)
PositionedID insertPositioned(FGPositioned::Type ty, const string &ident, const string &name, const SGGeod &pos, PositionedID apt, bool spatialIndex)
void reset(sqlite3_stmt_ptr stmt)
sqlite3_stmt_ptr findAirwayNet
bool isCachedFileModified(const SGPath &path, bool verbose)
sqlite3_stmt_ptr writePropertyQuery
sqlite3_stmt_ptr prepareSQL(const std::string &sql)
sqlite3_stmt_ptr runwayLengthFtQuery
sqlite3_stmt_ptr findNavaidForRunway
std::set< Octree::Branch * > deferredOctreeUpdates
sqlite3_stmt_ptr insertNavaid
void removePositioned(PositionedID rowid)
sqlite3_stmt_ptr getAllAirports
sqlite3_stmt_ptr isPosInAirway
sqlite3_stmt_ptr findNavsByFreq
sqlite3_stmt_ptr rollbackTransactionStmt
sqlite3_stmt_ptr setRunwayILS
sqlite3_stmt_ptr loadRunwayStmt
sqlite3_stmt_ptr airwayEdgesFrom
unsigned int transactionLevel
record the levels of open transaction objects we have
sqlite3_stmt_ptr removePositionedQuery
FGPositioned * loadById(sqlite_int64 rowId, sqlite3_int64 &aptId)
void execSelect1(sqlite3_stmt_ptr stmt)
std::map< DatFileType, NavDataCache::DatFilesGroupInfo > datFilesInfo
sqlite3_stmt_ptr loadCommStation
bool areDatFilesModified(NavDataCache::DatFileType datFileType, bool verbose)
FGAirport * loadAirport(sqlite_int64 rowId, FGPositioned::Type ty, const string &id, const string &name, const SGGeod &pos)
sqlite3_stmt_ptr stampFileCache
sqlite3_stmt_ptr insertAirway
sqlite3_stmt_ptr clearProperty
sqlite3_stmt_ptr writePropertyMulti
std::unique_ptr< RebuildThread > rebuilder
PositionedCache cache
the actual cache of ID -> instances.
sqlite3_int64 execInsert(sqlite3_stmt_ptr stmt)
void finalize(sqlite3_stmt_ptr s)
PositionedID nextTransientId
sqlite3_stmt_ptr insertCommStation
sqlite3_stmt_ptr findClosestWithIdent
bool stepSelect(sqlite3_stmt_ptr stmt)
sqlite3_stmt_ptr setAirportMetar
sqlite3_stmt_ptr searchAirports
void findDatFiles(NavDataCache::DatFileType datFileType)
sqlite3_stmt_ptr insertPositionedQuery
sqlite3_stmt_ptr statCacheCheck
void runSQL(const string &sql)
sqlite3_stmt_ptr loadNavaid
sqlite3_stmt_ptr prepare(const string &sql)
CommStation * loadComm(sqlite3_int64 rowId, FGPositioned::Type ty, const string &id, const string &name, const SGGeod &pos, PositionedID airport)
sqlite3_stmt_ptr insertAirport
sqlite3_stmt_ptr insertTempPosQuery
sqlite3_stmt_ptr updateTempPos
sqlite3_stmt_ptr loadAirway
sqlite3_stmt_ptr insertRunway
sqlite3_stmt_ptr setRunwayReciprocal
double runwayLengthFt(PositionedID rwy)
ThreadedGUISearchPrivate()
PositionedIDVec results() const
ThreadedGUISearch(const std::string &term, bool onlyAirports)
Transaction(NavDataCache *cache)
PositionedID findILS(PositionedID airport, const std::string &runway, const std::string &navIdent)
Given an airport, and runway and ILS identifier, find the corresponding cache entry.
PositionedID insertNavaid(FGPositioned::Type ty, const std::string &ident, const std::string &name, const SGGeod &pos, int freq, int range, double multiuse, PositionedID apt, PositionedID runway)
void stampCacheFile(const SGPath &path, const std::string &sha={})
void writeStringProperty(const std::string &key, const std::string &value)
PositionedID insertRunway(FGPositioned::Type ty, const std::string &ident, const SGGeod &pos, PositionedID apt, double heading, double length, double width, double displacedThreshold, double stopway, int markings, int approach, int tdz, int reil, int surfaceCode, int shoulder_code, float smoothness, int center_lights, int edge_lights, int distance_remaining)
PositionedID createPOI(FGPositioned::Type ty, const std::string &ident, const SGGeod &aPos, const std::string &aName, bool transient)
static const std::string defaultDatFile[]
PositionedID insertCommStation(FGPositioned::Type ty, const std::string &name, const SGGeod &pos, int freq, int range, PositionedID apt)
const DatFilesGroupInfo & getDatFilesInfo(DatFileType datFileType) const
void clearDynamicPositioneds()
void writeIntProperty(const std::string &key, int value)
std::string readStringProperty(const std::string &key)
void defineOctreeNode(Octree::Branch *pr, Octree::Node *nd)
int readIntProperty(const std::string &key)
void writeDoubleProperty(const std::string &key, const double &value)
static bool isAnotherProcessRebuilding()
string_list readStringListProperty(const std::string &key)
FGPositionedRef findCommByFreq(int freqKhz, const SGGeod &pos, FGPositioned::Filter *filt)
Find the closest matching comm-station on a frequency, to a position.
static NavDataCache * createInstance()
PositionedID createTransientID()
void writeOrderedStringListProperty(const std::string &key, const string_list &values, const char *separator)
TypedPositionedVec getOctreeLeafChildren(int64_t octreeNodeId)
given an octree leaf, return all its child positioned items and their types
void setRunwayReciprocal(PositionedID runway, PositionedID recip)
unsigned int rebuildPhaseCompletionPercentage() const
PositionedIDVec findCommsByFreq(int freqKhz, const SGGeod &pos, FGPositioned::Filter *filt)
Find all on a frequency, sorted by distance from a position The filter with be used for both type ran...
PositionedID airportItemWithIdent(PositionedID apt, FGPositioned::Type ty, const std::string &ident)
find the first match item of the specified type and ident, at an airport
FGPositionedRef findClosestWithIdent(const std::string &aIdent, const SGGeod &aPos, FGPositioned::Filter *aFilter)
double readDoubleProperty(const std::string &key)
FGPositionedList findAllWithIdent(const std::string &ident, FGPositioned::Filter *filter, bool exact)
PositionedIDVec airwayWaypts(int id)
Waypoints on the airway.
void setAirportMetar(const std::string &icao, bool hasMetar)
update the metar flag associated with an airport
void writeStringListProperty(const std::string &key, const string_list &values)
static const std::string datTypeStr[]
PositionedID insertAirport(FGPositioned::Type ty, const std::string &ident, const std::string &name, const SGPath &sceneryPath)
int getOctreeBranchChildren(int64_t octreeNodeId)
Given an Octree node ID, return a bit-mask defining which of the child nodes exist.
PositionedID findNavaidForRunway(PositionedID runway, FGPositioned::Type ty)
Given a runway and type, find the corresponding navaid (ILS / GS / OM)
void updatePosition(PositionedID item, const SGGeod &pos)
Modify the position of an existing item.
AirwayRef loadAirway(int airwayID)
bool removePOI(FGPositionedRef wpt)
FGPositionedRef loadById(PositionedID guid)
retrieve an FGPositioned from the cache.
void setRunwayILS(PositionedID runway, PositionedID ils)
static NavDataCache * instance()
void insertTower(PositionedID airportId, const SGGeod &pos)
char ** searchAirportNamesAndIdents(const std::string &aFilter)
Helper to implement the AirportSearch widget.
bool isCachedFileModified(const SGPath &path) const
void insertEdge(int network, int airwayID, PositionedID from, PositionedID to)
insert an edge between two positioned nodes, into the network.
friend class RebuildThread
string_list readOrderedStringListProperty(const std::string &key, const char *separator)
bool isRebuildRequired()
predicate - check if the cache needs to be rebuilt.
RebuildPhase rebuild()
run the cache rebuild - returns the current phase or 'done'
FGPositionedList findAllWithName(const std::string &ident, FGPositioned::Filter *filter, bool exact)
PositionedIDVec findNavaidsByFreq(int freqKhz, const SGGeod &pos, FGPositioned::Filter *filt)
Find all navaids matching a particular frequency, sorted by range from the supplied position.
void setRebuildPhaseProgress(RebuildPhase ph, unsigned int percent=0)
bool isInAirwayNetwork(int network, PositionedID pos)
is the specified positioned a node on the network?
void updateListsOfDatFiles()
void setNavaidColocated(PositionedID navaid, PositionedID colocatedDME)
AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos)
retrive all the destination points reachable from a positioned in an airway
AirportRunwayPair findAirportRunway(const std::string &name)
given a navaid name (or similar) from apt.dat / nav.dat, find the corresponding airport and runway ID...
std::vector< SceneryLocation > SceneryLocationList
PositionedIDVec airportItemsOfType(PositionedID apt, FGPositioned::Type ty, FGPositioned::Type maxTy=FGPositioned::INVALID)
find all items of a specified type (or range of types) at an airport
int findAirway(int network, const std::string &aName, bool create)
void loadNav(const NavDataCache::SceneryLocation &sceneryLocation, std::size_t bytesReadSoFar, std::size_t totalSizeOfAllDatFiles)
void removeChild(PositionedID id)
void insertChild(FGPositioned::Type ty, PositionedID id)
Octree node base class, tracks its bounding box and provides various queries relating to it.
const SGBoxd & bbox() const
virtual Leaf * findLeafForPos(const SGVec3d &aPos) const =0
static Options * sharedInstance()
void loadPOIs(const NavDataCache::SceneryLocation &sceneryLocation, std::size_t bytesReadSoFar, std::size_t totalSizeOfAllDatFiles)
unsigned int completionPercent() const
RebuildThread(NavDataCache *cache)
void setProgress(NavDataCache::RebuildPhase ph, unsigned int percent)
NavDataCache::RebuildPhase currentPhase() const
std::vector< SGPath > PathList
std::vector< std::string > string_list
Node * globalTransientOctree()
Node * globalPersistentOctree()
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
std::vector< AirwayEdge > AirwayEdgeVec
static NavDataCache * static_instance
@ RebuildLockAlreadyLocked
bool metarDataLoad(const SGPath &metar_file)
SGSharedPtr< FGPositioned > FGPositionedRef
RebuildLockStatus accquireRebuildLock()
static void releaseRebuildLock()
SGSharedPtr< Airway > AirwayRef
static int static_rebuildLockFileFd
void addSentryBreadcrumb(const std::string &, const std::string &)
const std::string static_rebuildLockFile
std::pair< int, PositionedID > AirwayEdge
std::map< PositionedID, FGPositionedRef > PositionedCache
void sentryReportException(const std::string &, const std::string &)
void fatalMessageBoxThenExit(const std::string &caption, const std::string &msg, const std::string &moreText, int exitStatus, bool reportToSentry)
std::pair< PositionedID, PositionedID > AirportRunwayPair
a pair of airport ID, runway ID
std::vector< TypedPositioned > TypedPositionedVec
std::vector< PositionedID > PositionedIDVec
std::vector< FGPositionedRef > FGPositionedList
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
SceneryLocationList paths