1 /**
2  * FlightPlan.hxx - defines a full flight-plan object, including
3  * departure, cruise, arrival information and waypoints
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_FLIGHTPLAN_HXX
25 #define FG_FLIGHTPLAN_HXX
26 
27 #include <functional>
28 
29 #include <Navaids/route.hxx>
30 #include <Airports/airport.hxx>
31 
32 namespace flightgear
33 {
34 
35 class Transition;
36 class FlightPlan;
37 
38 typedef SGSharedPtr<FlightPlan> FlightPlanRef;
39 
40 enum class ICAOFlightRules
41 {
42     VFR = 0,
43     IFR,
44     IFR_VFR,    // type Y
45     VFR_IFR     // type Z
46 };
47 
48 enum class ICAOFlightType
49 {
50     Scheduled = 0,
51     NonScheduled,
52     GeneralAviation,
53     Military,
54     Other // type X
55 };
56 
57 class FlightPlan : public RouteBase
58 {
59 public:
60   FlightPlan();
61   virtual ~FlightPlan();
62 
63   virtual std::string ident() const;
64   void setIdent(const std::string& s);
65 
66     // propogate the GPS/FMS setting for this through to the RoutePath
67     void setFollowLegTrackToFixes(bool tf);
68     bool followLegTrackToFixes() const;
69 
70     // aircraft approach category as per CFR 97.3, etc
71     // http://www.flightsimaviation.com/data/FARS/part_97-3.html
72     std::string icaoAircraftCategory() const;
73     void setIcaoAircraftCategory(const std::string& cat);
74 
icaoAircraftType() const75     std::string icaoAircraftType() const
76     { return _aircraftType; }
77 
78     void setIcaoAircraftType(const std::string& ty);
79 
80     FlightPlan* clone(const std::string& newIdent = std::string()) const;
81 
82   /**
83    * flight-plan leg encapsulation
84    */
85   class Leg : public SGReferenced
86   {
87   public:
owner() const88     FlightPlan* owner() const
89     { return const_cast<FlightPlan*>(_parent); }
90 
waypoint() const91     Waypt* waypoint() const
92     { return _waypt; }
93 
94     // return the next leg after this one
95     Leg* nextLeg() const;
96 
97     /**
98      * requesting holding at the waypoint upon reaching it. This will
99      * convert the waypt to a Hold if not already defined as one, but
100      * with default hold data.
101      *
102      * If the waypt is not of a type suitable for holding at, returns false
103      * (eg a runway or dynamic waypoint)
104      */
105     bool setHoldCount(int count);
106 
107     int holdCount() const;
108 
109 
110     bool convertWaypointToHold();
111 
112     unsigned int index() const;
113 
114     int altitudeFt() const;
115     int speed() const;
116 
117     int speedKts() const;
118     double speedMach() const;
119 
120     RouteRestriction altitudeRestriction() const;
121     RouteRestriction speedRestriction() const;
122 
123     void setSpeed(RouteRestriction ty, double speed);
124     void setAltitude(RouteRestriction ty, int altFt);
125 
126     double courseDeg() const;
127     double distanceNm() const;
128     double distanceAlongRoute() const;
129 
130     /**
131      * helper function, if the waypoint is modified in some way, to
132      * notify the flightplan owning this leg, and hence any delegates
133      * obsering us
134      */
135     void markWaypointDirty();
136   private:
137     friend class FlightPlan;
138 
139     Leg(FlightPlan* owner, WayptRef wpt);
140 
141     Leg* cloneFor(FlightPlan* owner) const;
142 
143       void writeToProperties(SGPropertyNode* node) const;
144 
145     const FlightPlan* _parent;
146     RouteRestriction _speedRestrict = RESTRICT_NONE,
147       _altRestrict = RESTRICT_NONE;
148     int _speed = 0;
149     int _altitudeFt = 0;
150 
151     // if > 0, we will hold at the waypoint using
152     // the published hold side/course
153     // This only works if _waypt is a Hold, either defined by a procedure
154     // or modified to become one
155     int _holdCount = 0;
156 
157     WayptRef _waypt;
158     /// length of this leg following the flown path
159     mutable double _pathDistance = -1.0;
160     mutable double _courseDeg = -1.0;
161     /// total distance of this leg from departure point
162     mutable double _distanceAlongPath = 11.0;
163   };
164 
165   using LegRef = SGSharedPtr<Leg>;
166 
167   class DelegateFactory;
168   using DelegateFactoryRef = std::shared_ptr<DelegateFactory>;
169 
170   class Delegate
171   {
172   public:
173     virtual ~Delegate();
174 
departureChanged()175     virtual void departureChanged() { }
arrivalChanged()176     virtual void arrivalChanged() { }
waypointsChanged()177     virtual void waypointsChanged() { }
cruiseChanged()178     virtual void cruiseChanged()  { }
cleared()179     virtual void cleared() { }
activated()180     virtual void activated() { }
181 
182       /**
183        * Invoked when the C++ code determines the active leg is done / next
184        * leg should be sequenced. The default route-manager delegate will
185        * advance to the next waypoint when handling this.
186        *
187        * If multiple delegates are installed, take special care not to sequence
188        * the waypoint twice.
189        */
sequence()190     virtual void sequence() { }
191 
currentWaypointChanged()192     virtual void currentWaypointChanged() { }
endOfFlightPlan()193     virtual void endOfFlightPlan() { }
194 
loaded()195       virtual void loaded() { }
196   protected:
197     Delegate();
198 
199   private:
200     friend class FlightPlan;
201 
202     // record the factory which created us, so we have the option to clean up
203     DelegateFactoryRef _factory;
204   };
205 
206   LegRef insertWayptAtIndex(Waypt* aWpt, int aIndex);
207   void insertWayptsAtIndex(const WayptVec& wps, int aIndex);
208 
209   void deleteIndex(int index);
210   void clearAll();
211   void clearLegs();
212   int clearWayptsWithFlag(WayptFlag flag);
213 
currentIndex() const214   int currentIndex() const
215   { return _currentIndex; }
216 
217   void sequence();
218 
219   void setCurrentIndex(int index);
220 
221   void activate();
222 
223   void finish();
224 
225     bool isActive() const;
226 
227   LegRef currentLeg() const;
228   LegRef nextLeg() const;
229   LegRef previousLeg() const;
230 
numLegs() const231   int numLegs() const
232   { return static_cast<int>(_legs.size()); }
233 
234   LegRef legAtIndex(int index) const;
235 
236   int findWayptIndex(const SGGeod& aPos) const;
237   int findWayptIndex(const FGPositionedRef aPos) const;
238 
239     int indexOfFirstNonDepartureWaypoint() const;
240     int indexOfFirstArrivalWaypoint() const;
241     int indexOfFirstApproachWaypoint() const;
242     int indexOfDestinationRunwayWaypoint() const;
243 
244   bool load(const SGPath& p);
245   bool save(const SGPath& p) const;
246 
247     bool save(std::ostream& stream) const;
248     bool load(std::istream& stream);
249 
departureAirport() const250   FGAirportRef departureAirport() const
251   { return _departure; }
252 
destinationAirport() const253   FGAirportRef destinationAirport() const
254   { return _destination; }
255 
departureRunway() const256   FGRunway* departureRunway() const
257   { return _departureRunway; }
258 
destinationRunway() const259   FGRunway* destinationRunway() const
260   { return _destinationRunway; }
261 
approach() const262   Approach* approach() const
263   { return _approach; }
264 
265   void setDeparture(FGAirport* apt);
266   void setDeparture(FGRunway* rwy);
267 
268     void clearDeparture();
269 
sid() const270   SID* sid() const
271   { return _sid; }
272 
273   Transition* sidTransition() const;
274 
275   void setSID(SID* sid, const std::string& transition = std::string());
276 
277   void setSID(Transition* sidWithTrans);
278 
279     void clearSID();
280 
281   void setDestination(FGAirport* apt);
282   void setDestination(FGRunway* rwy);
283 
284     void clearDestination();
285 
286     FGAirportRef alternate() const;
287     void setAlternate(FGAirportRef alt);
288 
289   /**
290     * note setting an approach will implicitly update the destination
291     * airport and runway to match
292     */
293     void setApproach(Approach* app, const std::string& transition = {});
294 
295     void setApproach(Transition* approachWithTrans);
296 
297 
298     Transition* approachTransition() const;
299 
star() const300   STAR* star() const
301   { return _star; }
302 
303   Transition* starTransition() const;
304 
305   void setSTAR(STAR* star, const std::string& transition = std::string());
306 
307   void setSTAR(Transition* starWithTrans);
308 
309   void clearSTAR();
310 
totalDistanceNm() const311   double totalDistanceNm() const
312   { return _totalDistance; }
313 
estimatedDurationMinutes() const314   int estimatedDurationMinutes() const
315   { return _estimatedDuration; }
316 
317   void setEstimatedDurationMinutes(int minutes);
318 
319   /**
320    * @brief computeDurationMinutes - use performance data and cruise data
321    * to estimate enroute time
322    */
323   void computeDurationMinutes();
324 
325   /**
326    * given a waypoint index, and an offset in NM, find the geodetic
327    * position on the route path. I.e the point 10nm before or after
328    * a particular waypoint.
329    */
330   SGGeod pointAlongRoute(int aIndex, double aOffsetNm) const;
331 
332   /**
333         given a waypoint index, find a point at a normalised offset, which must be [-1 .. 1]
334                 eg an offset of -0.5 will be half-way between aIndex and the preceeding waypoint,
335         and an offset of 0.3 will be 30% of the distance from aIndex to the next waypoint.
336      */
337   SGGeod pointAlongRouteNorm(int aIndex, double aOffsetNorm) const;
338 
339   /**
340    * Create a WayPoint from a string in the following format:
341    *  - simple identifier
342    *  - decimal-lon,decimal-lat
343    *  - airport-id/runway-id
344    *  - navaid/radial-deg/offset-nm
345    */
346   WayptRef waypointFromString(const std::string& target);
347 
348     /**
349      * attempt to replace the route waypoints (and potentially the SID and
350      * STAR) based on an ICAO standard route string, i.e item 15.
351      * Returns true if the rotue was parsed successfully (and this flight
352      * plan modified accordingly) or false if the string could not be
353      * parsed.
354      */
355     bool parseICAORouteString(const std::string& routeData);
356 
357     std::string asICAORouteString() const;
358 
359 // ICAO flight-plan data
360     void setFlightRules(ICAOFlightRules rules);
361     ICAOFlightRules flightRules() const;
362 
363     void setFlightType(ICAOFlightType type);
364     ICAOFlightType flightType() const;
365 
366     void setCallsign(const std::string& callsign);
callsign() const367     std::string callsign() const
368     { return _callsign; }
369 
370     void setRemarks(const std::string& remarks);
remarks() const371     std::string remarks() const
372     { return _remarks; }
373 
374 // cruise data
375     void setCruiseSpeedKnots(int kts);
376     int cruiseSpeedKnots() const;
377 
378     void setCruiseSpeedMach(double mach);
379     double cruiseSpeedMach() const;
380 
381     void setCruiseFlightLevel(int flightLevel);
382     int cruiseFlightLevel() const;
383 
384     void setCruiseAltitudeFt(int altFt);
385     int cruiseAltitudeFt() const;
386 
387   /**
388    * abstract interface for creating delegates automatically when a
389    * flight-plan is created or loaded
390    */
391   class DelegateFactory
392   {
393   public:
394     virtual Delegate* createFlightPlanDelegate(FlightPlan* fp) = 0;
395     virtual void destroyFlightPlanDelegate(FlightPlan* fp, Delegate* d);
396   };
397 
398   static void registerDelegateFactory(DelegateFactoryRef df);
399   static void unregisterDelegateFactory(DelegateFactoryRef df);
400 
401   void addDelegate(Delegate* d);
402   void removeDelegate(Delegate* d);
403 
404     using LegVisitor = std::function<void(Leg*)>;
405     void forEachLeg(const LegVisitor& lv);
406 private:
407   friend class Leg;
408 
409   int findLegIndex(const Leg* l) const;
410 
411   void lockDelegates();
412   void unlockDelegates();
413 
414   void notifyCleared();
415 
416   unsigned int _delegateLock = 0;
417   bool _arrivalChanged = false,
418     _departureChanged = false,
419     _waypointsChanged = false,
420     _currentWaypointChanged = false,
421     _cruiseDataChanged = false;
422   bool _didLoadFP = false;
423 
424     void saveToProperties(SGPropertyNode* d) const;
425 
426   bool loadXmlFormat(const SGPath& path);
427   bool loadGpxFormat(const SGPath& path);
428   bool loadPlainTextFormat(const SGPath& path);
429 
430   bool loadVersion1XMLRoute(SGPropertyNode_ptr routeData);
431   bool loadVersion2XMLRoute(SGPropertyNode_ptr routeData);
432   void loadXMLRouteHeader(SGPropertyNode_ptr routeData);
433   WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP);
434 
435   double magvarDegAt(const SGGeod& pos) const;
436   bool parseICAOLatLon(const std::string &s, SGGeod &p);
437 
438   std::string _ident;
439   std::string _callsign;
440   std::string _remarks;
441   std::string _aircraftType;
442 
443   int _currentIndex;
444     bool _followLegTrackToFix;
445     char _aircraftCategory;
446     ICAOFlightType _flightType = ICAOFlightType::Other;
447     ICAOFlightRules _flightRules = ICAOFlightRules::VFR;
448     int _cruiseAirspeedKnots = 0;
449     double _cruiseAirspeedMach = 0.0;
450     int _cruiseFlightLevel = 0;
451     int _cruiseAltitudeFt = 0;
452     int _estimatedDuration = 0;
453 
454   FGAirportRef _departure, _destination;
455   FGAirportRef _alternate;
456   FGRunway* _departureRunway, *_destinationRunway;
457   SGSharedPtr<SID> _sid;
458   SGSharedPtr<STAR> _star;
459   SGSharedPtr<Approach> _approach;
460   std::string _sidTransition, _starTransition, _approachTransition;
461 
462   double _totalDistance;
463   void rebuildLegData();
464 
465   using LegVec = std::vector<LegRef>;
466   LegVec _legs;
467 
468   std::vector<Delegate*> _delegates;
469 };
470 
471 } // of namespace flightgear
472 
473 #endif // of FG_FLIGHTPLAN_HXX
474