1 /**
2  * NavDataCache.hxx - defines a unified binary cache for navigation
3  * data, parsed from various text / XML sources.
4  */
5 
6 // Written by James Turner, started 2012.
7 //
8 // Copyright (C) 2012 James Turner
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 // General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23 
24 #ifndef FG_NAVDATACACHE_HXX
25 #define FG_NAVDATACACHE_HXX
26 
27 #include <memory>
28 #include <cstddef>                   // for std::size_t
29 #include <functional>
30 
31 #include <simgear/misc/strutils.hxx> // for string_list
32 #include <Navaids/positioned.hxx>
33 #include <Main/globals.hxx>          // for PathList
34 
35 class SGPath;
36 class FGRunway;
37 
38 namespace flightgear
39 {
40 
41 /// a pair of airport ID, runway ID
42 typedef std::pair<PositionedID, PositionedID> AirportRunwayPair;
43 
44 typedef std::pair<FGPositioned::Type, PositionedID> TypedPositioned;
45 typedef std::vector<TypedPositioned> TypedPositionedVec;
46 
47 // pair of airway ID, destination node ID
48 typedef std::pair<int, PositionedID> AirwayEdge;
49 typedef std::vector<AirwayEdge> AirwayEdgeVec;
50 
51 namespace Octree {
52   class Node;
53   class Branch;
54 }
55 
56     class Airway;
57     using AirwayRef = SGSharedPtr<Airway>;
58 
59 class NavDataCache
60 {
61 public:
62     ~NavDataCache();
63 
64 // singleton accessor
65     static NavDataCache* instance();
66 
67 // static creator
68     static NavDataCache* createInstance();
69 
70     static void shutdown();
71 
72     SGPath path() const;
73 
74     enum DatFileType {
75       DATFILETYPE_APT = 0,
76       DATFILETYPE_METAR,
77       DATFILETYPE_AWY,
78       DATFILETYPE_NAV,
79       DATFILETYPE_FIX,
80       DATFILETYPE_POI,
81       DATFILETYPE_CARRIER,
82       DATFILETYPE_TACAN_FREQ,
83       DATFILETYPE_LAST
84     };
85 
86     struct DatFilesGroupInfo {
87       DatFileType datFileType;      // for instance, DATFILETYPE_APT
88       PathList paths;               // SGPath instances
89       std::size_t totalSize;        // total size of all these files, in bytes
90     };
91 
92     // datTypeStr[DATFILETYPE_APT] = std::string("apt"), etc. This gives,
93     // among other things, the subdirectory of $scenery_path/NavData where
94     // each type of dat file is looked for.
95     static const std::string datTypeStr[];
96     // defaultDatFile[DATFILETYPE_APT] = std::string("Airports/apt.dat.gz"),
97     // etc. This tells where to find the historical dat files: those under
98     // $FG_ROOT.
99     static const std::string defaultDatFile[];
100 
101     // Update d->datFilesInfo and legacy d->metarDatPath, d->poiDatPath,
102     // etc. by looking into $scenery_path/NavData for each scenery path.
103     void updateListsOfDatFiles();
104     // Returns datFilesInfo for the given type.
105     const DatFilesGroupInfo& getDatFilesInfo(DatFileType datFileType) const;
106 
107   /**
108    * predicate - check if the cache needs to be rebuilt.
109    * This can happen is the cache file is missing or damaged, or one of the
110    ** global input files is changed.
111    */
112     bool isRebuildRequired();
113 
114     static bool isAnotherProcessRebuilding();
115 
116     enum RebuildPhase
117     {
118         REBUILD_UNKNOWN = 0,
119         REBUILD_READING_APT_DAT_FILES,
120         REBUILD_LOADING_AIRPORTS,
121         REBUILD_NAVAIDS,
122         REBUILD_FIXES,
123         REBUILD_POIS,
124         REBUILD_DONE
125     };
126 
127   /**
128    * run the cache rebuild - returns the current phase or 'done'
129    */
130   RebuildPhase rebuild();
131 
132   unsigned int rebuildPhaseCompletionPercentage() const;
133   void setRebuildPhaseProgress(RebuildPhase ph, unsigned int percent = 0);
134 
135   bool isCachedFileModified(const SGPath& path) const;
136   void stampCacheFile(const SGPath& path);
137 
138   int readIntProperty(const std::string& key);
139   double readDoubleProperty(const std::string& key);
140   std::string readStringProperty(const std::string& key);
141 
142   void writeIntProperty(const std::string& key, int value);
143   void writeStringProperty(const std::string& key, const std::string& value);
144   void writeDoubleProperty(const std::string& key, const double& value);
145 
146   // Warning: order is not necessarily preserved upon write-read cycles!
147   string_list readStringListProperty(const std::string& key);
148   void writeStringListProperty(const std::string& key, const string_list& values);
149 
150   // These ones guarantee the same order after each write-read cycle.
151   string_list readOrderedStringListProperty(const std::string& key,
152                                             const char* separator);
153   void writeOrderedStringListProperty(const std::string& key,
154                                       const string_list& values,
155                                       const char* separator);
156 
157   /**
158    * retrieve an FGPositioned from the cache.
159    * This may be trivial if the object is previously loaded, or require actual
160    * disk IO.
161    */
162   FGPositionedRef loadById(PositionedID guid);
163 
164   PositionedID insertAirport(FGPositioned::Type ty, const std::string& ident,
165                              const std::string& name);
166   void insertTower(PositionedID airportId, const SGGeod& pos);
167   PositionedID insertRunway(FGPositioned::Type ty, const std::string& ident,
168                           const SGGeod& pos, PositionedID apt,
169                           double heading, double length, double width, double displacedThreshold,
170                           double stopway, int surfaceCode);
171   void setRunwayReciprocal(PositionedID runway, PositionedID recip);
172   void setRunwayILS(PositionedID runway, PositionedID ils);
173 
174   PositionedID insertNavaid(FGPositioned::Type ty, const std::string& ident,
175                             const std::string& name, const SGGeod& pos, int freq, int range, double multiuse,
176                             PositionedID apt, PositionedID runway);
177 
178   // Assign colocated DME to a navaid
179   void setNavaidColocated(PositionedID navaid, PositionedID colocatedDME);
180 
181   PositionedID insertCommStation(FGPositioned::Type ty,
182                                  const std::string& name, const SGGeod& pos, int freq, int range,
183                                 PositionedID apt);
184   PositionedID insertFix(const std::string& ident, const SGGeod& aPos);
185 
186   PositionedID createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos);
187 
188   bool removePOI(FGPositioned::Type ty, const std::string& aIdent);
189 
190   /// update the metar flag associated with an airport
191   void setAirportMetar(const std::string& icao, bool hasMetar);
192 
193   /**
194    * Modify the position of an existing item.
195    */
196   void updatePosition(PositionedID item, const SGGeod &pos);
197 
198   FGPositionedList findAllWithIdent( const std::string& ident,
199                                      FGPositioned::Filter* filter,
200                                      bool exact );
201   FGPositionedList findAllWithName( const std::string& ident,
202                                     FGPositioned::Filter* filter,
203                                     bool exact );
204 
205   FGPositionedRef findClosestWithIdent( const std::string& aIdent,
206                                         const SGGeod& aPos,
207                                         FGPositioned::Filter* aFilter );
208 
209 
210   /**
211    * Helper to implement the AirportSearch widget. Optimised text search of
212    * airport names and idents, returning a list suitable for passing directly
213    * to PLIB.
214    */
215   char** searchAirportNamesAndIdents(const std::string& aFilter);
216 
217   /**
218    * Find the closest matching comm-station on a frequency, to a position.
219    * The filter with be used for both type ranging and to validate the result
220    * candidates.
221    */
222   FGPositionedRef findCommByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt);
223 
224   /**
225    * find all items of a specified type (or range of types) at an airport
226    */
227   PositionedIDVec airportItemsOfType(PositionedID apt, FGPositioned::Type ty,
228                                      FGPositioned::Type maxTy = FGPositioned::INVALID);
229 
230   /**
231    * find the first match item of the specified type and ident, at an airport
232    */
233   PositionedID airportItemWithIdent(PositionedID apt, FGPositioned::Type ty, const std::string& ident);
234 
235   /**
236    * Find all navaids matching a particular frequency, sorted by range from the
237    * supplied position. Type-range will be determined from the filter
238    */
239   PositionedIDVec findNavaidsByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt);
240 
241   /// overload version of the above that does not consider positioned when
242   /// returning results. Only used by TACAN carrier search
243   PositionedIDVec findNavaidsByFreq(int freqKhz, FGPositioned::Filter* filt);
244 
245   /**
246    * Given a runway and type, find the corresponding navaid (ILS / GS / OM)
247    */
248   PositionedID findNavaidForRunway(PositionedID runway, FGPositioned::Type ty);
249 
250   /**
251    * given a navaid name (or similar) from apt.dat / nav.dat, find the
252    * corresponding airport and runway IDs.
253    * Such names look like: 'LHBP 31L DME-ILS' or 'UEEE 23L MM'
254    */
255   AirportRunwayPair findAirportRunway(const std::string& name);
256 
257   /**
258    * Given an aiport, and runway and ILS identifier, find the corresponding cache
259    * entry. This matches the data we get in the ils.xml files for airports.
260    */
261   PositionedID findILS(PositionedID airport, const std::string& runway, const std::string& navIdent);
262 
263   /**
264    * Given an Octree node ID, return a bit-mask defining which of the child
265    * nodes exist. In practice this means an 8-bit value be sufficent, but
266    * an int works fine too.
267    */
268   int getOctreeBranchChildren(int64_t octreeNodeId);
269 
270   void defineOctreeNode(Octree::Branch* pr, Octree::Node* nd);
271 
272   /**
273    * given an octree leaf, return all its child positioned items and their types
274    */
275   TypedPositionedVec getOctreeLeafChildren(int64_t octreeNodeId);
276 
277 // airways
278   int findAirway(int network, const std::string& aName, bool create);
279 
280   int findAirway(const std::string& aName);
281 
282   /**
283    * insert an edge between two positioned nodes, into the network.
284    * The airway identifier will be set accordingly. No reverse edge is created
285    * by this method - edges are directional so a reverses must be explicitly
286    * created.
287    */
288   void insertEdge(int network, int airwayID, PositionedID from, PositionedID to);
289 
290   /// is the specified positioned a node on the network?
291   bool isInAirwayNetwork(int network, PositionedID pos);
292 
293   /**
294    * retrive all the destination points reachable from a positioned
295    * in an airway
296    */
297   AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos);
298 
299     AirwayRef loadAirway(int airwayID);
300 
301     /**
302      * Waypoints on the airway
303      */
304   PositionedIDVec airwayWaypts(int id);
305 
306     class Transaction
307     {
308     public:
309         Transaction(NavDataCache* cache);
310         ~Transaction();
311 
312         void commit();
313     private:
314         NavDataCache* _instance;
315         bool _committed;
316     };
317 
318     bool isReadOnly() const;
319 
320     class ThreadedGUISearch
321     {
322     public:
323         ThreadedGUISearch(const std::string& term, bool onlyAirports);
324         ~ThreadedGUISearch();
325 
326         PositionedIDVec results() const;
327 
328         bool isComplete() const;
329     private:
330         class ThreadedGUISearchPrivate;
331         std::unique_ptr<ThreadedGUISearchPrivate> d;
332     };
333 
334     void clearDynamicPositioneds();
335 
336 private:
337   NavDataCache();
338 
339   friend class RebuildThread;
340 
341   // A generic function for loading all navigation data files of the
342   // specified type (apt/fix/nav etc.) using the passed type-specific loader.
343   void loadDatFiles(DatFileType type,
344                     std::function<void(const SGPath&, std::size_t, std::size_t)> loader);
345 
346   void doRebuild();
347 
348   friend class Transaction;
349 
350     void beginTransaction();
351     void commitTransaction();
352     void abortTransaction();
353 
354   class NavDataCachePrivate;
355   std::unique_ptr<NavDataCachePrivate> d;
356 
357   bool rebuildInProgress = false;
358 };
359 
360 } // of namespace flightgear
361 
362 #endif // of FG_NAVDATACACHE_HXX
363