FlightGear next
NasalFlightPlan.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: NasalFlightPlan.cxx
3 * SPDX-FileComment: expose FlightPlan classes to Nasal
4 * SPDX-FileCopyrightText: Copyright (C) 2020 James Turner
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "config.h"
9
10#include <algorithm>
11#include <cstring>
12#include <optional>
13
14#include "NasalFlightPlan.hxx"
15
16#include "NasalPositioned.hxx"
17
18#include <Airports/airport.hxx>
19#include <Airports/dynamics.hxx>
20#include <Airports/parking.hxx>
21#include <Airports/runways.hxx>
23#include <Main/fg_props.hxx>
24#include <Main/globals.hxx>
27#include <Navaids/airways.hxx>
28#include <Navaids/fix.hxx>
29#include <Navaids/navrecord.hxx>
30#include <Navaids/procedure.hxx>
31#include <Navaids/routePath.hxx>
32#include <Navaids/waypoint.hxx>
33#include <Scenery/scenery.hxx>
35
36using namespace flightgear;
37
38static void wayptGhostDestroy(void* g);
39static void legGhostDestroy(void* g);
40static void routeBaseGhostDestroy(void* g);
41
42static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out);
43static void waypointGhostSetMember(naContext c, void* g, naRef field, naRef value);
44
45static naGhostType WayptGhostType = {wayptGhostDestroy,
46 "waypoint",
49
50static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out);
51static void legGhostSetMember(naContext c, void* g, naRef field, naRef value);
52
53static naGhostType FPLegGhostType = {legGhostDestroy,
54 "flightplan-leg",
57
58static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out);
59static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value);
60
65
66static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out);
68 "procedure",
70 0};
71
72static const char* airwayGhostGetMember(naContext c, void* g, naRef field, naRef* out);
74 "airway",
76 0};
77
78static void hashset(naContext c, naRef hash, const char* key, naRef val)
79{
80 naRef s = naNewString(c);
81 naStr_fromdata(s, (char*)key, strlen(key));
82 naHash_set(hash, s, val);
83}
84
85static naRef stringToNasal(naContext c, const std::string& s)
86{
87 return naStr_fromdata(naNewString(c),
88 const_cast<char*>(s.c_str()),
89 s.length());
90}
91
92static bool convertToNum(naRef v, double& result)
93{
94 naRef n = naNumValue(v);
95 if (naIsNil(n)) {
96 return false; // couldn't convert
97 }
98
99 result = n.num;
100 return true;
101}
102
103static WayptFlag wayptFlagFromString(const char* s)
104{
105 if (!strcmp(s, "sid")) return WPT_DEPARTURE;
106 if (!strcmp(s, "star")) return WPT_ARRIVAL;
107 if (!strcmp(s, "approach")) return WPT_APPROACH;
108 if (!strcmp(s, "missed")) return WPT_MISS;
109 if (!strcmp(s, "pseudo")) return WPT_PSEUDO;
110
111 return (WayptFlag)0;
112}
113
114static naRef wayptFlagToNasal(naContext c, unsigned int flags)
115{
116 if (flags & WPT_PSEUDO) return stringToNasal(c, "pseudo");
117 if (flags & WPT_DEPARTURE) return stringToNasal(c, "sid");
118 if (flags & WPT_ARRIVAL) return stringToNasal(c, "star");
119 if (flags & WPT_MISS) return stringToNasal(c, "missed");
120 if (flags & WPT_APPROACH) return stringToNasal(c, "approach");
121 return naNil();
122}
123
124static std::optional<Airway::Level> airwayLevelFromNasal(naRef na)
125{
126 if (naIsString(na)) {
127 const char* s = naStr_data(na);
128 if (!strcmp(s, "both")) return {Airway::Both};
129 if (!strcmp(s, "high")) return {Airway::HighLevel};
130 if (!strcmp(s, "low")) return {Airway::LowLevel};
131 return {};
132 }
133
134 if (!naIsNum(na)) {
135 return {};
136 }
137
138 const int num = static_cast<int>(na.num);
139 switch (num) {
141 case Airway::LowLevel: return {Airway::LowLevel};
142 case Airway::Both: return {Airway::Both};
143 default:
144 break;
145 // fall through
146 }
147
148 return {}; // fail
149}
150
152{
153 if (naGhost_type(r) == &WayptGhostType)
154 return (Waypt*)naGhost_ptr(r);
155
156 if (naGhost_type(r) == &FPLegGhostType) {
157 FlightPlan::Leg* leg = (FlightPlan::Leg*)naGhost_ptr(r);
158 return leg->waypoint();
159 }
160
161 return 0;
162}
163
164static void wayptGhostDestroy(void* g)
165{
166 Waypt* wpt = (Waypt*)g;
167 if (!Waypt::put(wpt)) // unref
168 delete wpt;
169}
170
171static void legGhostDestroy(void* g)
172{
174 if (!FlightPlan::Leg::put(leg)) // unref
175 delete leg;
176}
177
178
180{
181 if (naGhost_type(r) == &FPLegGhostType)
182 return (FlightPlan::Leg*)naGhost_ptr(r);
183 return 0;
184}
185
187{
188 if (naGhost_type(r) == &ProcedureGhostType)
189 return (Procedure*)naGhost_ptr(r);
190 return 0;
191}
192
194{
195 if (naGhost_type(r) == &FlightPlanGhostType)
196 return (FlightPlan*)naGhost_ptr(r);
197 return 0;
198}
199
200static Airway* airwayGhost(naRef r)
201{
202 if (naGhost_type(r) == &AirwayGhostType)
203 return (Airway*)naGhost_ptr(r);
204 return 0;
205}
206
207static void routeBaseGhostDestroy(void* g)
208{
209 RouteBase* r = (RouteBase*)g;
210 if (!RouteBase::put(r)) // unref
211 delete r;
212}
213
215static naRef fpLegPrototype;
217static naRef airwayPrototype;
218
219naRef ghostForWaypt(naContext c, const Waypt* wpt)
220{
221 if (!wpt) {
222 return naNil();
223 }
224
225 Waypt::get(wpt); // take a ref
226 return naNewGhost2(c, &WayptGhostType, (void*)wpt);
227}
228
229naRef ghostForLeg(naContext c, const FlightPlan::Leg* leg)
230{
231 if (!leg) {
232 return naNil();
233 }
234
235 FlightPlan::Leg::get(leg); // take a ref
236 return naNewGhost2(c, &FPLegGhostType, (void*)leg);
237}
238
239naRef ghostForFlightPlan(naContext c, const FlightPlan* fp)
240{
241 if (!fp) {
242 return naNil();
243 }
244
245 FlightPlan::get(fp); // take a ref
246 return naNewGhost2(c, &FlightPlanGhostType, (void*)fp);
247}
248
249naRef ghostForProcedure(naContext c, const Procedure* proc)
250{
251 if (!proc) {
252 return naNil();
253 }
254
255 FlightPlan::get(proc); // take a ref
256 return naNewGhost2(c, &ProcedureGhostType, (void*)proc);
257}
258
259naRef ghostForAirway(naContext c, const Airway* awy)
260{
261 if (!awy) {
262 return naNil();
263 }
264
265 Airway::get(awy); // take a ref
266 return naNewGhost2(c, &AirwayGhostType, (void*)awy);
267}
268
269// Return the navaid ghost associated with a waypoint of navaid type.
270static naRef waypointNavaid(naContext c, Waypt* wpt)
271{
272 FGPositioned* pos = wpt->source();
273 if (!pos || (!FGNavRecord::isNavaidType(pos) && !fgpositioned_cast<FGFix>(pos))) {
274 return naNil();
275 }
276
277 return ghostForPositioned(c, wpt->source());
278}
279
280// Return the airport ghost associated with a waypoint of airport or runway
281// type.
282static naRef waypointAirport(naContext c, Waypt* wpt)
283{
284 FGPositioned* pos = wpt->source();
285
287 pos = static_cast<FGRunway*>(pos)->airport();
288 } else if (!FGPositioned::isAirportType(pos)) {
289 return naNil();
290 }
291
292 return ghostForAirport(c, static_cast<FGAirport*>(pos));
293}
294
295// Return the runway ghost associated with a waypoint of runway type.
296static naRef waypointRunway(naContext c, Waypt* wpt)
297{
298 FGPositioned* pos = wpt->source();
299
300 if (!FGPositioned::isRunwayType(pos)) {
301 return naNil();
302 }
303
304 return ghostForRunway(c, static_cast<FGRunway*>(pos));
305}
306
307static const char* waypointCommonGetMember(naContext c, Waypt* wpt, const char* fieldName, naRef* out)
308{
309 if (!strcmp(fieldName, "wp_name") || !strcmp(fieldName, "id"))
310 *out = stringToNasal(c, wpt->ident());
311 else if (!strcmp(fieldName, "wp_type"))
312 *out = stringToNasal(c, wpt->type());
313 else if (!strcmp(fieldName, "wp_role"))
314 *out = wayptFlagToNasal(c, wpt->flags());
315 else if (!strcmp(fieldName, "wp_lat") || !strcmp(fieldName, "lat"))
316 *out = naNum(wpt->position().getLatitudeDeg());
317 else if (!strcmp(fieldName, "wp_lon") || !strcmp(fieldName, "lon"))
318 *out = naNum(wpt->position().getLongitudeDeg());
319 else if (!strcmp(fieldName, "wp_parent_name")) {
320 if (wpt->owner()) {
321 *out = stringToNasal(c, wpt->owner()->ident());
322 } else {
323 *out = naNil();
324 }
325 } else if (!strcmp(fieldName, "wp_parent")) {
326 // TODO add ghostForRouteElement to cover all this
327 Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
328 if (proc) {
329 *out = ghostForProcedure(c, proc);
330 } else {
331 Airway* airway = dynamic_cast<Airway*>(wpt->owner());
332 if (airway) {
333 *out = ghostForAirway(c, airway);
334 } else {
335 *out = naNil();
336 }
337 }
338 } else if (!strcmp(fieldName, "fly_type")) {
339 if (wpt->type() == "hold") {
340 *out = stringToNasal(c, "Hold");
341 } else {
342 *out = stringToNasal(c, wpt->flag(WPT_OVERFLIGHT) ? "flyOver" : "flyBy");
343 }
344 } else if (!strcmp(fieldName, "heading_course")) {
345 *out = naNum(wpt->headingRadialDeg());
346 } else if (!strcmp(fieldName, "navaid")) {
347 *out = waypointNavaid(c, wpt);
348 } else if (!strcmp(fieldName, "airport")) {
349 *out = waypointAirport(c, wpt);
350 } else if (!strcmp(fieldName, "runway")) {
351 *out = waypointRunway(c, wpt);
352 } else if (!strcmp(fieldName, "airway")) {
353 if (wpt->type() == "via") {
354 AirwayRef awy = static_cast<Via*>(wpt)->airway();
355 assert(awy);
356 *out = ghostForAirway(c, awy);
357 } else {
358 Airway* airway = dynamic_cast<Airway*>(wpt->owner());
359 if (airway) {
360 *out = ghostForAirway(c, airway);
361 } else {
362 *out = naNil();
363 }
364 }
365 } else if (!strcmp(fieldName, "hidden")) {
366 *out = naNum(wpt->flag(WPT_HIDDEN));
367 } else if (wpt->type() == "hold") {
368 // hold-specific properties
369 const auto hold = static_cast<Hold*>(wpt);
370 if (!strcmp(fieldName, "hold_is_left_handed")) {
371 *out = naNum(hold->isLeftHanded());
372 } else if (!strcmp(fieldName, "hold_is_distance")) {
373 *out = naNum(hold->isDistance());
374 } else if (!strcmp(fieldName, "hold_is_time")) {
375 *out = naNum(!hold->isDistance());
376 } else if (!strcmp(fieldName, "hold_inbound_radial")) {
377 *out = naNum(hold->inboundRadial());
378 } else if (!strcmp(fieldName, "hold_heading_radial_deg")) {
379 *out = naNum(hold->inboundRadial());
380 } else if (!strcmp(fieldName, "hold_time_or_distance")) {
381 // This is the leg length, defined either as a time in seconds, or a
382 // distance in nm.
383 *out = naNum(hold->timeOrDistance());
384 } else {
385 return nullptr; // member not found
386 }
387 } else {
388 return nullptr; // member not found
389 }
390
391 return "";
392}
393
394static bool waypointCommonSetMember(naContext c, Waypt* wpt, const char* fieldName, naRef value)
395{
396 if (!strcmp(fieldName, "wp_role")) {
397 if (!naIsString(value)) naRuntimeError(c, "wp_role must be a string");
398 if (wpt->owner() != NULL) naRuntimeError(c, "cannot override wp_role on waypoint with parent");
399 WayptFlag f = wayptFlagFromString(naStr_data(value));
400 if (f == 0) {
401 naRuntimeError(c, "unrecognized wp_role value %s", naStr_data(value));
402 }
403
404 wpt->setFlag(f, true);
405 } else if (!strcmp(fieldName, "fly_type")) {
406 if (!naIsString(value)) naRuntimeError(c, "fly_type must be a string");
407 bool flyOver = (strcmp(naStr_data(value), "flyOver") == 0);
408 wpt->setFlag(WPT_OVERFLIGHT, flyOver);
409 } else if (!strcmp(fieldName, "hidden")) {
410 if (!naIsNum(value)) naRuntimeError(c, "wpt.hidden must be a number");
411 wpt->setFlag(WPT_HIDDEN, static_cast<int>(value.num) != 0);
412 } else if (wpt->type() == "hold") {
413 const auto hold = static_cast<Hold*>(wpt);
414 if (!strcmp(fieldName, "hold_heading_radial_deg")) {
415 if (!naIsNum(value)) naRuntimeError(c, "set hold_heading_radial_deg: invalid hold radial");
416 hold->setHoldRadial(value.num);
417 } else if (!strcmp("hold_is_left_handed", fieldName)) {
418 bool leftHanded = static_cast<int>(value.num) > 0;
419 if (leftHanded) {
420 hold->setLeftHanded();
421 } else {
422 hold->setRightHanded();
423 }
424 }
425 } else {
426 // nothing changed
427 return false;
428 }
429
430 return true;
431}
432
433static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out)
434{
435 const char* fieldName = naStr_data(field);
436 Waypt* wpt = (flightgear::Waypt*)g;
437 return waypointCommonGetMember(c, wpt, fieldName, out);
438}
439
441{
442 if (naIsNil(arg) || !naIsString(arg)) {
443 return RESTRICT_NONE;
444 }
445
446 const std::string u = simgear::strutils::lowercase(naStr_data(arg));
447 if (u == "computed") return RESTRICT_COMPUTED;
448 if (u == "at") return RESTRICT_AT;
449 if (u == "above") return RESTRICT_ABOVE;
450 if (u == "below") return RESTRICT_BELOW;
451 if (u == "mach") return SPEED_RESTRICT_MACH;
452 if (u == "computed-mach") return SPEED_COMPUTED_MACH;
453 if (u == "delete") return RESTRICT_DELETE;
454 if (u == "between") return RESTRICT_BETWEEN;
455 return RESTRICT_NONE;
456};
457
459{
460 if (naIsNil(arg) || !naIsString(arg)) {
461 return DEFAULT_UNITS;
462 }
463
464 const auto u = simgear::strutils::lowercase(naStr_data(arg));
465 if ((u == "knots") || (u == "kt")) return SPEED_KNOTS;
466 if (u == "kph") return SPEED_KPH;
467 if (u == "mach") return SPEED_MACH;
468 if ((u == "ft") || (u == "\'") || (u == "feet")) return ALTITUDE_FEET;
469 if ((u == "m") || (u == "meter") || (u == "meters")) return ALTITUDE_METER;
470 if ((u == "fl") || (u == "flight-level")) return ALTITUDE_FLIGHTLEVEL;
471
472 throw sg_format_exception("routeUnitsFromArg: unknown units", u);
473}
474
476{
477 switch (rr) {
478 case RESTRICT_NONE: return naNil();
479 case RESTRICT_AT: return stringToNasal(c, "at");
480 case RESTRICT_ABOVE: return stringToNasal(c, "above");
481 case RESTRICT_BELOW: return stringToNasal(c, "below");
482 case SPEED_RESTRICT_MACH: return stringToNasal(c, "mach");
483 case RESTRICT_COMPUTED: return stringToNasal(c, "computed");
484 case SPEED_COMPUTED_MACH: return stringToNasal(c, "computed-mach");
485 case RESTRICT_DELETE: return stringToNasal(c, "delete");
486 case RESTRICT_BETWEEN: return stringToNasal(c, "between");
487 }
488
489 return naNil();
490}
491
492// navaid() method of FPLeg ghosts
493static naRef f_fpLeg_navaid(naContext c, naRef me, int argc, naRef* args)
494{
496 if (!w) {
497 naRuntimeError(c,
498 "flightplan-leg.navaid() called, but can't find the "
499 "underlying waypoint for the flightplan-leg object");
500 }
501
502 return waypointNavaid(c, w);
503}
504
505// airport() method of FPLeg ghosts
506static naRef f_fpLeg_airport(naContext c, naRef me, int argc, naRef* args)
507{
509 if (!w) {
510 naRuntimeError(c,
511 "flightplan-leg.airport() called, but can't find the "
512 "underlying waypoint for the flightplan-leg object");
513 }
514
515 return waypointAirport(c, w);
516}
517
518// runway() method of FPLeg ghosts
519static naRef f_fpLeg_runway(naContext c, naRef me, int argc, naRef* args)
520{
522 if (!w) {
523 naRuntimeError(c,
524 "flightplan-leg.runway() called, but can't find the "
525 "underlying waypoint for the flightplan-leg object");
526 }
527
528 return waypointRunway(c, w);
529}
530
531static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out)
532{
533 const char* fieldName = naStr_data(field);
535 if (!leg) {
536 *out = naNil();
537 naRuntimeError(c, "leg ghost member fetched, but no associated leg object found");
538 return "";
539 }
540
541 Waypt* wpt = leg->waypoint();
542
543 if (!strcmp(fieldName, "parents")) {
544 *out = naNewVector(c);
545 naVec_append(*out, fpLegPrototype);
546 } else if (!strcmp(fieldName, "index")) {
547 *out = naNum(leg->index());
548 } else if (!strcmp(fieldName, "alt_cstr")) {
549 *out = naNum(leg->altitudeFt());
550 } else if (!strcmp(fieldName, "alt_cstr_type")) {
552 } else if (!strcmp(fieldName, "speed_cstr")) {
553 double s = isMachRestrict(leg->speedRestriction()) ? leg->speedMach() : leg->speedKts();
554 *out = naNum(s);
555 } else if (!strcmp(fieldName, "speed_cstr_type")) {
557 } else if (!strcmp(fieldName, "leg_distance")) {
558 *out = naNum(leg->distanceNm());
559 } else if (!strcmp(fieldName, "leg_bearing")) {
560 *out = naNum(leg->courseDeg());
561 } else if (!strcmp(fieldName, "distance_along_route")) {
562 *out = naNum(leg->distanceAlongRoute());
563 } else if (!strcmp(fieldName, "airport")) {
564 *out = naNewFunc(c, naNewCCode(c, f_fpLeg_airport));
565 } else if (!strcmp(fieldName, "navaid")) {
566 *out = naNewFunc(c, naNewCCode(c, f_fpLeg_navaid));
567 } else if (!strcmp(fieldName, "runway")) {
568 *out = naNewFunc(c, naNewCCode(c, f_fpLeg_runway));
569 } else if (!strcmp(fieldName, "hold_count")) {
570 *out = naNum(leg->holdCount());
571 } else { // check for fields defined on the underlying waypoint
572 if (wpt) { // FIXME null check shouldn't be required, check refcount
573 return waypointCommonGetMember(c, wpt, fieldName, out);
574 } else {
575 naRuntimeError(c, "leg ghost member fetched, but no underlying waypoint object found");
576 }
577 }
578
579 return ""; // success
580}
581
582static void waypointGhostSetMember(naContext c, void* g, naRef field, naRef value)
583{
584 const char* fieldName = naStr_data(field);
585 Waypt* wpt = (Waypt*)g;
586 waypointCommonSetMember(c, wpt, fieldName, value);
587}
588
589static void legGhostSetMember(naContext c, void* g, naRef field, naRef value)
590{
591 const char* fieldName = naStr_data(field);
593
594 bool didChange = false;
595 if (!strcmp(fieldName, "hold_count")) {
596 const int count = static_cast<int>(value.num);
597 // this may upgrade the waypoint to a hold
598 if (!leg->setHoldCount(count))
599 naRuntimeError(c, "unable to set hold on leg waypoint: maybe unsuitable waypt type?");
600 } else if (!strcmp(fieldName, "hold_heading_radial_deg")) {
601 if (!leg->convertWaypointToHold())
602 naRuntimeError(c, "couldn't convert leg waypoint into a hold");
603
604 // now we can call the base method
605 didChange = waypointCommonSetMember(c, leg->waypoint(), fieldName, value);
606 } else {
607 didChange = waypointCommonSetMember(c, leg->waypoint(), fieldName, value);
608 }
609
610 if (didChange) {
611 leg->markWaypointDirty();
612 }
613}
614
615static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out)
616{
617 const char* fieldName = naStr_data(field);
618 FlightPlan* fp = static_cast<FlightPlan*>(g);
619
620 if (!strcmp(fieldName, "parents")) {
621 *out = naNewVector(c);
622 naVec_append(*out, flightplanPrototype);
623 } else if (!strcmp(fieldName, "id"))
624 *out = stringToNasal(c, fp->ident());
625 else if (!strcmp(fieldName, "departure"))
626 *out = ghostForAirport(c, fp->departureAirport());
627 else if (!strcmp(fieldName, "destination"))
628 *out = ghostForAirport(c, fp->destinationAirport());
629 else if (!strcmp(fieldName, "departure_runway"))
630 *out = ghostForRunway(c, fp->departureRunway());
631 else if (!strcmp(fieldName, "destination_runway"))
632 *out = ghostForRunway(c, fp->destinationRunway());
633 else if (!strcmp(fieldName, "sid"))
634 *out = ghostForProcedure(c, fp->sid());
635 else if (!strcmp(fieldName, "sid_trans"))
636 *out = ghostForProcedure(c, fp->sidTransition());
637 else if (!strcmp(fieldName, "star"))
638 *out = ghostForProcedure(c, fp->star());
639 else if (!strcmp(fieldName, "star_trans"))
640 *out = ghostForProcedure(c, fp->starTransition());
641 else if (!strcmp(fieldName, "approach"))
642 *out = ghostForProcedure(c, fp->approach());
643 else if (!strcmp(fieldName, "approach_trans"))
644 *out = ghostForProcedure(c, fp->approachTransition());
645 else if (!strcmp(fieldName, "current"))
646 *out = naNum(fp->currentIndex());
647 else if (!strcmp(fieldName, "aircraftCategory"))
648 *out = stringToNasal(c, fp->icaoAircraftCategory());
649 else if (!strcmp(fieldName, "followLegTrackToFix"))
650 *out = naNum(fp->followLegTrackToFixes());
651 else if (!strcmp(fieldName, "active"))
652 *out = naNum(fp->isActive());
653 else if (!strcmp(fieldName, "cruiseAltitudeFt"))
654 *out = naNum(fp->cruiseAltitudeFt());
655 else if (!strcmp(fieldName, "cruiseFlightLevel"))
656 *out = naNum(fp->cruiseFlightLevel());
657 else if (!strcmp(fieldName, "cruiseAltitudeM"))
658 *out = naNum(fp->cruiseAltitudeM());
659 else if (!strcmp(fieldName, "cruiseSpeedKt"))
660 *out = naNum(fp->cruiseSpeedKnots());
661 else if (!strcmp(fieldName, "cruiseSpeedMach"))
662 *out = naNum(fp->cruiseSpeedMach());
663 else if (!strcmp(fieldName, "cruiseSpeedKPH"))
664 *out = naNum(fp->cruiseSpeedKPH());
665 else if (!strcmp(fieldName, "remarks"))
666 *out = stringToNasal(c, fp->remarks());
667 else if (!strcmp(fieldName, "callsign"))
668 *out = stringToNasal(c, fp->callsign());
669 else if (!strcmp(fieldName, "estimatedDurationMins"))
670 *out = naNum(fp->estimatedDurationMinutes());
671 else if (!strcmp(fieldName, "firstNonDepartureLeg"))
672 *out = naNum(fp->indexOfFirstNonDepartureWaypoint());
673 else if (!strcmp(fieldName, "firstArrivalLeg"))
674 *out = naNum(fp->indexOfFirstArrivalWaypoint());
675 else if (!strcmp(fieldName, "firstApproachLeg"))
676 *out = naNum(fp->indexOfFirstApproachWaypoint());
677 else if (!strcmp(fieldName, "destination_runway_leg"))
678 *out = naNum(fp->indexOfDestinationRunwayWaypoint());
679 else if (!strcmp(fieldName, "totalDistanceNm"))
680 *out = naNum(fp->totalDistanceNm());
681 else if (!strcmp(fieldName, "isRoute"))
682 *out = naNum(fp->isRoute());
683
684 else {
685 return nullptr;
686 }
687
688 return "";
689}
690
691static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value)
692{
693 const char* fieldName = naStr_data(field);
694 FlightPlan* fp = static_cast<FlightPlan*>(g);
695
696 if (!strcmp(fieldName, "id")) {
697 if (!naIsString(value)) naRuntimeError(c, "flightplan.id must be a string");
698 fp->setIdent(naStr_data(value));
699 } else if (!strcmp(fieldName, "current")) {
700 int index = static_cast<int>(value.num);
701 if ((index < -1) || (index >= fp->numLegs())) {
702 naRuntimeError(c, "flightplan.current must be a valid index or -1");
703 }
704 fp->setCurrentIndex(index);
705 } else if (!strcmp(fieldName, "departure")) {
706 FGAirport* apt = airportGhost(value);
707 if (apt) {
708 fp->setDeparture(apt);
709 return;
710 }
711
712 FGRunway* rwy = runwayGhost(value);
713 if (rwy) {
714 fp->setDeparture(rwy);
715 return;
716 }
717
718 if (naIsNil(value)) {
719 fp->clearDeparture();
720 return;
721 }
722
723 naRuntimeError(c, "bad argument type setting departure");
724 } else if (!strcmp(fieldName, "destination")) {
725 FGAirport* apt = airportGhost(value);
726 if (apt) {
727 fp->setDestination(apt);
728 return;
729 }
730
731 FGRunway* rwy = runwayGhost(value);
732 if (rwy) {
733 fp->setDestination(rwy);
734 return;
735 }
736
737 if (naIsNil(value)) {
738 fp->clearDestination();
739 return;
740 }
741
742 naRuntimeError(c, "bad argument type setting destination");
743 } else if (!strcmp(fieldName, "departure_runway")) {
744 FGRunway* rwy = runwayGhost(value);
745 if (rwy) {
746 fp->setDeparture(rwy);
747 return;
748 }
749
750 naRuntimeError(c, "bad argument type setting departure runway");
751 } else if (!strcmp(fieldName, "destination_runway")) {
752 if (naIsNil(value)) {
753 fp->setDestination(static_cast<FGRunway*>(nullptr));
754 return;
755 }
756
757 FGRunway* rwy = runwayGhost(value);
758 if (rwy) {
759 fp->setDestination(rwy);
760 return;
761 }
762
763 naRuntimeError(c, "bad argument type setting destination runway");
764 } else if (!strcmp(fieldName, "sid")) {
765 Procedure* proc = procedureGhost(value);
766 if (proc && (proc->type() == PROCEDURE_SID)) {
767 fp->setSID((flightgear::SID*)proc);
768 return;
769 }
770 // allow a SID transition to be set, implicitly include the SID itself
771 if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
772 fp->setSID((Transition*)proc);
773 return;
774 }
775
776 if (naIsString(value)) {
777 const std::string s(naStr_data(value));
778 FGAirport* apt = fp->departureAirport();
779 auto sid = apt->findSIDWithIdent(s);
780 if (!sid) {
781 naRuntimeError(c, "Unknown SID %s at %s", s.c_str(), apt->ident().c_str());
782 }
783
784 fp->setSID(sid);
785 return;
786 }
787
788 if (naIsNil(value)) {
789 fp->clearSID();
790 return;
791 }
792
793 naRuntimeError(c, "bad argument type setting SID");
794 } else if (!strcmp(fieldName, "sid_trans")) {
795 Procedure* proc = procedureGhost(value);
796 if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
797 fp->setSID((Transition*)proc);
798 return;
799 }
800
801 if (naIsNil(value)) {
802 fp->setSID(fp->sid(), std::string{});
803 return;
804 }
805
806 if (naIsString(value)) {
807 const std::string s(naStr_data(value));
808 Transition* trans = nullptr;
809
810 if (fp->sid()) {
811 trans = fp->sid()->findTransitionByName(s);
812 if (!trans) {
813 naRuntimeError(c, "No such transition %s for SID %s at %s",
814 s.c_str(),
815 fp->sid()->ident().c_str(),
816 fp->departureAirport()->ident().c_str());
817 }
818 } else {
819 trans = fp->departureAirport()->selectSIDByTransition(fp->departureRunway(), s);
820 if (!trans) {
821 naRuntimeError(c, "Couldn't find SID transition to %s at %s",
822 s.c_str(),
823 fp->departureAirport()->ident().c_str());
824 }
825 }
826
827 if (trans) {
828 fp->setSID(trans);
829 }
830
831 return;
832 }
833
834 naRuntimeError(c, "bad argument type setting sid_trans");
835 } else if (!strcmp(fieldName, "star")) {
836 Procedure* proc = procedureGhost(value);
837 if (proc && (proc->type() == PROCEDURE_STAR)) {
838 fp->setSTAR((STAR*)proc);
839 return;
840 }
841
842 if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
843 fp->setSTAR((Transition*)proc);
844 return;
845 }
846
847 if (naIsString(value)) {
848 const std::string s(naStr_data(value));
849 FGAirport* apt = fp->destinationAirport();
850 auto star = apt->findSTARWithIdent(s);
851 if (!star) {
852 naRuntimeError(c, "Unknown SID %s at %s", s.c_str(), apt->ident().c_str());
853 }
854 fp->setSTAR(star);
855 return;
856 }
857
858 if (naIsNil(value)) {
859 fp->clearSTAR();
860 return;
861 }
862
863 naRuntimeError(c, "bad argument type setting STAR");
864 } else if (!strcmp(fieldName, "star_trans")) {
865 Procedure* proc = procedureGhost(value);
866 if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
867 fp->setSTAR((Transition*)proc);
868 return;
869 }
870
871 if (naIsNil(value)) {
872 fp->setSTAR(fp->star(), std::string{});
873 return;
874 }
875
876 if (naIsString(value)) {
877 const std::string s(naStr_data(value));
878 Transition* trans = nullptr;
879
880 if (fp->star()) {
881 trans = fp->star()->findTransitionByName(s);
882 if (!trans) {
883 naRuntimeError(c, "No such transition %s for STAR %s at %s",
884 s.c_str(),
885 fp->star()->ident().c_str(),
886 fp->destinationAirport()->ident().c_str());
887 }
888 } else {
889 trans = fp->destinationAirport()->selectSTARByTransition(fp->destinationRunway(), s);
890 if (!trans) {
891 naRuntimeError(c, "Couldn't find STAR transition to %s at %s",
892 s.c_str(),
893 fp->destinationAirport()->ident().c_str());
894 }
895 }
896
897 if (trans) {
898 fp->setSTAR(trans);
899 }
900
901 return;
902 }
903
904 naRuntimeError(c, "bad argument type setting star_trans");
905 } else if (!strcmp(fieldName, "approach")) {
906 Procedure* proc = procedureGhost(value);
907 if (proc && Approach::isApproach(proc->type())) {
908 fp->setApproach((Approach*)proc);
909 return;
910 }
911
912 if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
913 fp->setApproach((Transition*)proc);
914 return;
915 }
916
917 if (naIsString(value)) {
918 FGAirport* apt = fp->destinationAirport();
919 fp->setApproach(apt->findApproachWithIdent(naStr_data(value)));
920 return;
921 }
922
923 if (naIsNil(value)) {
924 fp->setApproach(static_cast<Approach*>(nullptr));
925 return;
926 }
927
928 naRuntimeError(c, "bad argument type setting approach");
929 } else if (!strcmp(fieldName, "approach_trans")) {
930 Procedure* proc = procedureGhost(value);
931 if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
932 fp->setApproach((Transition*)proc);
933 return;
934 }
935
936 if (naIsNil(value)) {
937 fp->setApproach(fp->approach(), std::string{});
938 return;
939 }
940
941 if (naIsString(value)) {
942 const std::string s(naStr_data(value));
943 Transition* trans = nullptr;
944
945 if (fp->approach()) {
946 trans = fp->approach()->findTransitionByName(s);
947 if (!trans) {
948 naRuntimeError(c, "No such transition %s for approach %s at %s",
949 s.c_str(),
950 fp->approach()->ident().c_str(),
951 fp->destinationAirport()->ident().c_str());
952 }
953 } else {
954 naRuntimeError(c, "No approach selected, can't set approach_trans");
955 }
956
957 if (trans) {
958 fp->setApproach(trans);
959 }
960
961 return;
962 }
963
964 naRuntimeError(c, "bad argument type setting approach_trans");
965 } else if (!strcmp(fieldName, "aircraftCategory")) {
966 if (!naIsString(value)) naRuntimeError(c, "aircraftCategory must be a string");
967 fp->setIcaoAircraftCategory(naStr_data(value));
968 } else if (!strcmp(fieldName, "followLegTrackToFix")) {
969 fp->setFollowLegTrackToFixes(static_cast<bool>(value.num));
970 } else if (!strcmp(fieldName, "cruiseAltitudeFt")) {
971 fp->setCruiseAltitudeFt(static_cast<int>(value.num));
972 } else if (!strcmp(fieldName, "cruiseAltitudeM")) {
973 fp->setCruiseAltitudeM(static_cast<int>(value.num));
974 } else if (!strcmp(fieldName, "cruiseFlightLevel")) {
975 fp->setCruiseFlightLevel(static_cast<int>(value.num));
976 } else if (!strcmp(fieldName, "cruiseSpeedKt")) {
977 fp->setCruiseSpeedKnots(static_cast<int>(value.num));
978 } else if (!strcmp(fieldName, "cruiseSpeedKPH")) {
979 fp->setCruiseSpeedKPH(static_cast<int>(value.num));
980 } else if (!strcmp(fieldName, "cruiseSpeedMach")) {
981 fp->setCruiseSpeedMach(value.num);
982 } else if (!strcmp(fieldName, "callsign")) {
983 if (!naIsString(value)) naRuntimeError(c, "flightplan.callsign must be a string");
984 fp->setCallsign(naStr_data(value));
985 } else if (!strcmp(fieldName, "remarks")) {
986 if (!naIsString(value)) naRuntimeError(c, "flightplan.remarks must be a string");
987 fp->setRemarks(naStr_data(value));
988 } else if (!strcmp(fieldName, "estimatedDurationMins")) {
989 fp->setEstimatedDurationMinutes(static_cast<int>(value.num));
990 }
991}
992
993static naRef procedureTpType(naContext c, ProcedureType ty)
994{
995 switch (ty) {
996 case PROCEDURE_SID: return stringToNasal(c, "sid");
997 case PROCEDURE_STAR: return stringToNasal(c, "star");
998 case PROCEDURE_TRANSITION: return stringToNasal(c, "transition");
999 case PROCEDURE_RUNWAY_TRANSITION: return stringToNasal(c, "rwy_transition");
1004 return stringToNasal(c, "IAP");
1005 default:
1006 return naNil();
1007 }
1008}
1009
1010static naRef procedureRadioType(naContext c, ProcedureType ty)
1011{
1012 switch (ty) {
1013 case PROCEDURE_APPROACH_VOR: return stringToNasal(c, "VOR");
1014 case PROCEDURE_APPROACH_ILS: return stringToNasal(c, "ILS");
1015 case PROCEDURE_APPROACH_RNAV: return stringToNasal(c, "RNAV");
1016 case PROCEDURE_APPROACH_NDB: return stringToNasal(c, "NDB");
1017 default:
1018 return naNil();
1019 }
1020}
1021
1022static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out)
1023{
1024 const char* fieldName = naStr_data(field);
1025 Procedure* proc = (Procedure*)g;
1026
1027 if (!strcmp(fieldName, "parents")) {
1028 *out = naNewVector(c);
1029 naVec_append(*out, procedurePrototype);
1030 } else if (!strcmp(fieldName, "id"))
1031 *out = stringToNasal(c, proc->ident());
1032 else if (!strcmp(fieldName, "airport"))
1033 *out = ghostForAirport(c, proc->airport());
1034 else if (!strcmp(fieldName, "tp_type"))
1035 *out = procedureTpType(c, proc->type());
1036 else if (!strcmp(fieldName, "radio"))
1037 *out = procedureRadioType(c, proc->type());
1038 else if (!strcmp(fieldName, "runways")) {
1039 *out = naNewVector(c);
1040 for (FGRunwayRef rwy : proc->runways()) {
1041 naVec_append(*out, stringToNasal(c, rwy->ident()));
1042 }
1043 } else if (!strcmp(fieldName, "transitions")) {
1044 const auto ty = proc->type();
1045 string_list idents;
1046 if (Approach::isApproach(ty)) {
1047 const auto app = static_cast<Approach*>(proc);
1048 idents = app->transitionIdents();
1049 } else if ((ty == PROCEDURE_SID) || (ty == PROCEDURE_STAR)) {
1050 ArrivalDeparture* ad = static_cast<ArrivalDeparture*>(proc);
1051 idents = ad->transitionIdents();
1052 } else {
1053 *out = naNil();
1054 return "";
1055 }
1056
1057 *out = naNewVector(c);
1058 for (std::string id : idents) {
1059 naVec_append(*out, stringToNasal(c, id));
1060 }
1061 } else {
1062 return 0;
1063 }
1064
1065 return "";
1066}
1067
1068static const char* airwayGhostGetMember(naContext c, void* g, naRef field, naRef* out)
1069{
1070 const char* fieldName = naStr_data(field);
1071 Airway* awy = (Airway*)g;
1072
1073 if (!strcmp(fieldName, "parents")) {
1074 *out = naNewVector(c);
1075 naVec_append(*out, airwayPrototype);
1076 } else if (!strcmp(fieldName, "id"))
1077 *out = stringToNasal(c, awy->ident());
1078 else if (!strcmp(fieldName, "level")) {
1079 // James was dumb, returning a string here since we didn't have the
1080 // constant values exposed.
1081 const auto level = awy->level();
1082 switch (level) {
1083 case Airway::HighLevel: *out = stringToNasal(c, "high"); break;
1084 case Airway::LowLevel: *out = stringToNasal(c, "low"); break;
1085 case Airway::Both: *out = stringToNasal(c, "both"); break;
1086 default: *out = naNil();
1087 }
1088 } else if (!strcmp(fieldName, "level_code")) {
1089 // so expose the numerical values here
1090 const auto level = awy->level();
1091 *out = naNum(static_cast<int>(level));
1092 } else {
1093 return 0;
1094 }
1095
1096 return "";
1097}
1098
1099static naRef f_createFlightplan(naContext c, naRef me, int argc, naRef* args)
1100{
1101 bool asRoute = false; // default to a regular Flight-Plan
1102 if (argc == 1) {
1103 if (naIsNum(args[0])) {
1104 asRoute = args[0].num != 0.0;
1105 }
1106 } else if ((argc == 2) && naIsNum(args[1])) {
1107 // args[0] is a string file name, args[1] is our flag
1108 asRoute = args[1].num != 0.0;
1109 }
1110
1112
1113 if ((argc > 0) && naIsString(args[0])) {
1114 SGPath path(naStr_data(args[0]));
1115 if (!path.exists()) {
1116 std::string pdata = path.utf8Str();
1117 naRuntimeError(c, "createFlightplan, no file at path %s", pdata.c_str());
1118 }
1119
1120 if (!fp->load(path)) {
1121 SG_LOG(SG_NASAL, SG_WARN, "failed to load flight-plan from " << path);
1122 return naNil();
1123 }
1124 }
1125
1126 return ghostForFlightPlan(c, fp.get());
1127}
1128
1129static naRef f_flightplan(naContext c, naRef me, int argc, naRef* args)
1130{
1131 if (argc == 0) {
1132 auto rm = globals->get_subsystem<FGRouteMgr>();
1133 return ghostForFlightPlan(c, rm->flightPlan());
1134 }
1135
1136 if ((argc > 0) && naIsString(args[0])) {
1137 return f_createFlightplan(c, me, argc, args);
1138 }
1139
1140 naRuntimeError(c, "bad arguments to flightplan()");
1141 return naNil();
1142}
1143
1145{
1146public:
1147 NasalFPDelegate(FlightPlan* fp, FGNasalSys* sys, naRef ins) : _nasal(sys),
1148 _plan(fp),
1149 _instance(ins)
1150 {
1151 assert(fp);
1152 assert(sys);
1153 _gcSaveKey = _nasal->gcSave(ins);
1154 }
1155
1157 {
1158 _nasal->gcRelease(_gcSaveKey);
1159 }
1160
1161 void departureChanged() override
1162 {
1163 callDelegateMethod("departureChanged");
1164 }
1165
1166 void arrivalChanged() override
1167 {
1168 callDelegateMethod("arrivalChanged");
1169 }
1170
1171 void waypointsChanged() override
1172 {
1173 callDelegateMethod("waypointsChanged");
1174 }
1175
1177 {
1178 callDelegateMethod("currentWaypointChanged");
1179 }
1180
1181 void cleared() override
1182 {
1183 callDelegateMethod("cleared");
1184 }
1185
1186 void endOfFlightPlan() override
1187 {
1188 callDelegateMethod("endOfFlightPlan");
1189 }
1190
1191 void activated() override
1192 {
1193 callDelegateMethod("activated");
1194 }
1195
1196 void sequence() override
1197 {
1198 callDelegateMethod("sequence");
1199 }
1200
1201 void loaded() override
1202 {
1203 callDelegateMethod("loaded");
1204 }
1205
1206private:
1207 void callDelegateMethod(const char* method)
1208 {
1209 naRef f;
1210 naContext ctx = naNewContext();
1211
1212 if (naMember_cget(ctx, _instance, method, &f) != 0) {
1213 naRef arg[1];
1214 arg[0] = ghostForFlightPlan(ctx, _plan);
1215 _nasal->callMethod(f, _instance, 1, arg, naNil());
1216 }
1217
1218 naFreeContext(ctx);
1219 }
1220
1221 FGNasalSys* _nasal;
1222 FlightPlan* _plan;
1223 naRef _instance;
1224 int _gcSaveKey;
1225};
1226
1227
1229{
1230public:
1231 NasalFPDelegateFactory(naRef code, const std::string& id) : _id(id)
1232 {
1233 _nasal = globals->get_subsystem<FGNasalSys>();
1234 _func = code;
1235 _gcSaveKey = _nasal->gcSave(_func);
1236 }
1237
1239 {
1240 _nasal->gcRelease(_gcSaveKey);
1241 }
1242
1244 {
1245 naRef args[1];
1246 naContext ctx = naNewContext();
1247 args[0] = ghostForFlightPlan(ctx, fp);
1248 naRef instance = _nasal->call(_func, 1, args, naNil());
1249
1250 FlightPlan::Delegate* result = nullptr;
1251 if (!naIsNil(instance)) {
1252 // will GC-save instance
1253 result = new NasalFPDelegate(fp, _nasal, instance);
1254 }
1255
1256 naFreeContext(ctx);
1257 return result;
1258 }
1259
1261 {
1262 delete d;
1263 }
1264
1265 const std::string& id() const
1266 {
1267 return _id;
1268 }
1269
1270private:
1271 const std::string _id;
1272 FGNasalSys* _nasal;
1273 naRef _func;
1274 int _gcSaveKey;
1275};
1276
1277static std::vector<FlightPlan::DelegateFactoryRef> static_nasalDelegateFactories;
1278
1286
1287static naRef f_registerFPDelegate(naContext c, naRef me, int argc, naRef* args)
1288{
1289 if ((argc < 1) || !naIsFunc(args[0])) {
1290 naRuntimeError(c, "non-function argument to registerFlightPlanDelegate");
1291 }
1292
1293 const std::string delegateId = (argc > 1) ? naStr_data(args[1]) : std::string{};
1294 if (!delegateId.empty()) {
1295 auto it = std::find_if(static_nasalDelegateFactories.begin(), static_nasalDelegateFactories.end(),
1296 [delegateId](FlightPlan::DelegateFactoryRef factory) {
1297 auto nfpd = static_cast<NasalFPDelegateFactory*>(factory.get());
1298 return nfpd->id() == delegateId;
1299 });
1300 if (it != static_nasalDelegateFactories.end()) {
1301 naRuntimeError(c, "duplicate delegate ID at registerFlightPlanDelegate: %s", delegateId.c_str());
1302 }
1303 }
1304
1305 auto factory = std::make_shared<NasalFPDelegateFactory>(args[0], delegateId);
1307 static_nasalDelegateFactories.push_back(factory);
1308 return naNil();
1309}
1310
1311static naRef f_unregisterFPDelegate(naContext c, naRef me, int argc, naRef* args)
1312{
1313 if ((argc < 1) || !naIsString(args[0])) {
1314 naRuntimeError(c, "non-string argument to unregisterFlightPlanDelegate");
1315 }
1316
1317 const std::string delegateId = naStr_data(args[0]);
1318 auto it = std::find_if(static_nasalDelegateFactories.begin(), static_nasalDelegateFactories.end(),
1319 [delegateId](FlightPlan::DelegateFactoryRef factory) {
1320 auto nfpd = static_cast<NasalFPDelegateFactory*>(factory.get());
1321 return nfpd->id() == delegateId;
1322 });
1323
1324 if (it == static_nasalDelegateFactories.end()) {
1325 SG_LOG(SG_NASAL, SG_DEV_WARN, "f_unregisterFPDelegate: no delegate with ID:" << delegateId);
1326 return naNil();
1327 }
1328
1331
1332 return naNil();
1333}
1334
1335static WayptRef wayptFromArg(naRef arg)
1336{
1337 WayptRef r = wayptGhost(arg);
1338 if (r.valid()) {
1339 return r;
1340 }
1341
1342 FGPositioned* pos = positionedGhost(arg);
1343 if (!pos) {
1344 // let's check if the arg is hash, coudl extra a geod and hence build
1345 // a simple waypoint
1346
1347 return WayptRef();
1348 }
1349
1350 // special-case for runways
1351 if (pos->type() == FGPositioned::RUNWAY) {
1352 return new RunwayWaypt((FGRunway*)pos, NULL);
1353 }
1354
1355 return new NavaidWaypoint(pos, NULL);
1356}
1357
1358static naRef convertWayptVecToNasal(naContext c, const WayptVec& wps)
1359{
1360 naRef result = naNewVector(c);
1361 for (WayptRef wpt : wps) {
1362 naVec_append(result, ghostForWaypt(c, wpt.get()));
1363 }
1364 return result;
1365}
1366
1367static naRef f_airwaySearch(naContext c, naRef me, int argc, naRef* args)
1368{
1369 if (argc < 2) {
1370 naRuntimeError(c, "airwaysSearch needs at least two arguments");
1371 }
1372
1373 WayptRef start = wayptFromArg(args[0]),
1374 end = wayptFromArg(args[1]);
1375
1376 if (!start || !end) {
1377 SG_LOG(SG_NASAL, SG_WARN, "airwaysSearch: start or end points are invalid");
1378 return naNil();
1379 }
1380
1381 bool highLevel = true;
1382 if ((argc > 2) && naIsString(args[2])) {
1383 if (!strcmp(naStr_data(args[2]), "lowlevel")) {
1384 highLevel = false;
1385 }
1386 }
1387
1388 WayptVec route;
1389 if (highLevel) {
1390 Airway::highLevel()->route(start, end, route);
1391 } else {
1392 Airway::lowLevel()->route(start, end, route);
1393 }
1394
1395 return convertWayptVecToNasal(c, route);
1396}
1397
1398static naRef f_findAirway(naContext c, naRef me, int argc, naRef* args)
1399{
1400 if ((argc < 1) || !naIsString(args[0])) {
1401 naRuntimeError(c, "findAirway needs at least one string arguments");
1402 }
1403
1404 std::string ident = naStr_data(args[0]);
1405 FGPositionedRef pos;
1407 if (argc >= 2) {
1408 int posArgIndex = 1;
1409
1410 // try next arg as a level specifier first: this means you can't use
1411 // a string navaid ident which is 'high', 'low' or 'both'.
1412 auto maybeLevel = airwayLevelFromNasal(args[1]);
1413 if (maybeLevel.has_value()) {
1414 level = maybeLevel.value_or(Airway::Both);
1415 ++posArgIndex; // worked, so increment index
1416 }
1417
1418 if (argc > posArgIndex) {
1419 pos = positionedFromArg(args[posArgIndex]);
1420 }
1421 }
1422
1423 AirwayRef awy;
1424 if (pos) {
1425 SG_LOG(SG_NASAL, SG_INFO, "Pevious navaid for airway():" << pos->ident());
1426 awy = Airway::findByIdentAndNavaid(ident, pos);
1427 } else {
1428 awy = Airway::findByIdent(ident, level);
1429 }
1430
1431 if (!awy)
1432 return naNil();
1433
1434 return ghostForAirway(c, awy.get());
1435}
1436
1437
1438static naRef f_createWP(naContext c, naRef me, int argc, naRef* args)
1439{
1440 SGGeod pos;
1441 int argOffset = geodFromArgs(args, 0, argc, pos);
1442
1443 if (((argc - argOffset) < 1) || !naIsString(args[argOffset])) {
1444 naRuntimeError(c, "createWP: no identifier supplied");
1445 }
1446
1447 std::string ident = naStr_data(args[argOffset++]);
1448 WayptRef wpt = new BasicWaypt(pos, ident, NULL);
1449
1450 // set waypt flags - approach, departure, pseudo, etc
1451 if (argc > argOffset) {
1452 WayptFlag f = wayptFlagFromString(naStr_data(args[argOffset++]));
1453 if (f == 0) {
1454 naRuntimeError(c, "createWP: bad waypoint role");
1455 }
1456
1457 wpt->setFlag(f);
1458 }
1459
1460 return ghostForWaypt(c, wpt);
1461}
1462
1463static naRef f_createWPFrom(naContext c, naRef me, int argc, naRef* args)
1464{
1465 if (argc < 1) {
1466 naRuntimeError(c, "createWPFrom: need at least one argument");
1467 }
1468
1469 FGPositioned* positioned = positionedGhost(args[0]);
1470 if (!positioned) {
1471 naRuntimeError(c, "createWPFrom: couldn't convert arg[0] to FGPositioned");
1472 }
1473
1474 WayptRef wpt;
1475 if (positioned->type() == FGPositioned::RUNWAY) {
1476 wpt = new RunwayWaypt((FGRunway*)positioned, NULL);
1477 } else {
1478 wpt = new NavaidWaypoint(positioned, NULL);
1479 }
1480
1481 // set waypt flags - approach, departure, pseudo, etc
1482 if (argc > 1) {
1483 WayptFlag f = wayptFlagFromString(naStr_data(args[1]));
1484 if (f == 0) {
1485 naRuntimeError(c, "createWPFrom: bad waypoint role");
1486 }
1487 wpt->setFlag(f);
1488 }
1489
1490 return ghostForWaypt(c, wpt);
1491}
1492
1493static naRef f_createViaTo(naContext c, naRef me, int argc, naRef* args)
1494{
1495 if ((argc < 2) || (argc > 3)) {
1496 naRuntimeError(c, "createViaTo: needs two or three arguments");
1497 }
1498
1499 AirwayRef airway = airwayGhost(args[0]);
1500 naRef toArg = args[1];
1501 if (!airway && naIsString(args[0])) {
1502 std::string airwayName = naStr_data(args[0]);
1503 auto level = Airway::Both;
1504 if (argc == 3) {
1505 // this means second arg is high / low select
1506 auto l = airwayLevelFromNasal(args[1]);
1507 toArg = args[2];
1508 if (!l) {
1509 naRuntimeError(c, "createViaTo: level argument is not accepted");
1510 }
1511
1512 level = l.value_or(Airway::Both);
1513 }
1514
1515
1516 airway = Airway::findByIdent(airwayName, level);
1517 if (!airway) {
1518 naRuntimeError(c, "createViaTo: couldn't find airway with provided name: %s",
1519 naStr_data(args[0]));
1520 }
1521 }
1522
1523 if (!airway) {
1524 naRuntimeError(c, "createViaTo: invalid airway");
1525 }
1526
1527 FGPositionedRef nav;
1528 if (naIsString(toArg)) {
1529 WayptRef enroute = airway->findEnroute(naStr_data(toArg));
1530 if (!enroute) {
1531 naRuntimeError(c, "unknown waypoint on airway %s: %s",
1532 naStr_data(args[0]), naStr_data(toArg));
1533 }
1534
1535 nav = enroute->source();
1536 } else {
1537 nav = positionedGhost(toArg);
1538 if (!nav) {
1539 naRuntimeError(c, "createViaTo: arg[1] is not a navaid");
1540 }
1541 }
1542
1543 if (!airway->containsNavaid(nav)) {
1544 naRuntimeError(c, "createViaTo: navaid not on airway");
1545 }
1546
1547 Via* via = new Via(nullptr, airway, nav);
1548 return ghostForWaypt(c, via);
1549}
1550
1551static naRef f_createViaFromTo(naContext c, naRef me, int argc, naRef* args)
1552{
1553 if ((argc < 3) || (argc > 4)) {
1554 naRuntimeError(c, "createViaFromTo: needs three or four arguments");
1555 }
1556
1557 auto from = positionedFromArg(args[0]);
1558 if (!from) {
1559 naRuntimeError(c, "createViaFromTo: from wp not found");
1560 }
1561
1562 AirwayRef airway = airwayGhost(args[1]);
1563 naRef toArg = args[2];
1564 if (!airway && naIsString(args[1])) {
1565 std::string airwayName = naStr_data(args[1]);
1566 auto level = Airway::Both;
1567 if (argc == 4) {
1568 // this means third arg is high / low select
1569 auto l = airwayLevelFromNasal(args[2]);
1570 toArg = args[3];
1571 if (!l) {
1572 naRuntimeError(c, "createViaFromTo: level argument is not accepted");
1573 }
1574
1575 level = l.value_or(Airway::Both);
1576 }
1577
1578
1579 airway = Airway::findByIdent(airwayName, level);
1580 if (!airway) {
1581 naRuntimeError(c, "createViaFromTo: couldn't find airway with provided name: %s",
1582 naStr_data(args[1]));
1583 }
1584 }
1585
1586 if (!airway) {
1587 naRuntimeError(c, "createViaFromTo: invalid airway");
1588 }
1589
1590 FGPositionedRef nav;
1591 if (naIsString(toArg)) {
1592 WayptRef enroute = airway->findEnroute(naStr_data(toArg));
1593 if (!enroute) {
1594 naRuntimeError(c, "unknown waypoint on airway %s: %s",
1595 naStr_data(args[1]), naStr_data(toArg));
1596 }
1597
1598 nav = enroute->source();
1599 } else {
1600 nav = positionedFromArg(toArg);
1601 if (!nav) {
1602 naRuntimeError(c, "createViaFromTo: final arg is not a navaid");
1603 }
1604 }
1605
1606 if (!airway->containsNavaid(nav)) {
1607 naRuntimeError(c, "createViaFromTo: navaid not on airway");
1608 }
1609
1610 Via* via = new Via(nullptr, airway, nav);
1611 return ghostForWaypt(c, via);
1612}
1613
1614static naRef f_createDiscontinuity(naContext c, naRef me, int argc, naRef* args)
1615{
1616 return ghostForWaypt(c, new Discontinuity(NULL));
1617}
1618
1619static naRef f_flightplan_getWP(naContext c, naRef me, int argc, naRef* args)
1620{
1621 FlightPlan* fp = flightplanGhost(me);
1622 if (!fp) {
1623 naRuntimeError(c, "flightplan.getWP called on non-flightplan object");
1624 }
1625
1626 int index;
1627 if (argc == 0) {
1628 index = fp->currentIndex();
1629 } else {
1630 index = (int)naNumValue(args[0]).num;
1631 }
1632
1633 if ((index < 0) || (index >= fp->numLegs())) {
1634 return naNil();
1635 }
1636
1637 return ghostForLeg(c, fp->legAtIndex(index));
1638}
1639
1640static naRef f_flightplan_currentWP(naContext c, naRef me, int argc, naRef* args)
1641{
1642 FlightPlan* fp = flightplanGhost(me);
1643 if (!fp) {
1644 naRuntimeError(c, "flightplan.currentWP called on non-flightplan object");
1645 }
1646
1647 int index = fp->currentIndex();
1648 if (argc > 0) {
1649 index += static_cast<int>(naNumValue(args[0]).num);
1650 }
1651
1652 if ((index < 0) || (index >= fp->numLegs())) {
1653 return naNil();
1654 }
1655
1656 return ghostForLeg(c, fp->legAtIndex(index));
1657}
1658
1659static naRef f_flightplan_nextWP(naContext c, naRef me, int argc, naRef* args)
1660{
1661 FlightPlan* fp = flightplanGhost(me);
1662 if (!fp) {
1663 naRuntimeError(c, "flightplan.nextWP called on non-flightplan object");
1664 }
1665 return ghostForLeg(c, fp->nextLeg());
1666}
1667
1668static naRef f_flightplan_numWaypoints(naContext c, naRef me, int argc, naRef* args)
1669{
1670 FlightPlan* fp = flightplanGhost(me);
1671 if (!fp) {
1672 naRuntimeError(c, "flightplan.numWaypoints called on non-flightplan object");
1673 }
1674 return naNum(fp->numLegs());
1675}
1676
1677static naRef f_flightplan_numRemainingWaypoints(naContext c, naRef me, int argc, naRef* args)
1678{
1679 FlightPlan* fp = flightplanGhost(me);
1680 if (!fp) {
1681 naRuntimeError(c, "flightplan.f_flightplan_numRemainingWaypoints called on non-flightplan object");
1682 }
1683
1684 // for an inactive flightplan, just reutnr the total number of WPs
1685 if (fp->currentIndex() < 0) {
1686 return naNum(fp->numLegs());
1687 }
1688
1689 return naNum(fp->numLegs() - fp->currentIndex());
1690}
1691
1692static naRef f_flightplan_appendWP(naContext c, naRef me, int argc, naRef* args)
1693{
1694 FlightPlan* fp = flightplanGhost(me);
1695 if (!fp) {
1696 naRuntimeError(c, "flightplan.appendWP called on non-flightplan object");
1697 }
1698
1699 WayptRef wp = wayptGhost(args[0]);
1700 int index = fp->numLegs();
1701 fp->insertWayptAtIndex(wp.get(), index);
1702 return naNum(index);
1703}
1704
1705static naRef f_flightplan_insertWP(naContext c, naRef me, int argc, naRef* args)
1706{
1707 FlightPlan* fp = flightplanGhost(me);
1708 if (!fp) {
1709 naRuntimeError(c, "flightplan.insertWP called on non-flightplan object");
1710 }
1711
1712 WayptRef wp = wayptGhost(args[0]);
1713 int index = -1; // append
1714 if ((argc > 1) && naIsNum(args[1])) {
1715 index = (int)args[1].num;
1716 }
1717
1718 auto leg = fp->insertWayptAtIndex(wp.get(), index);
1719 return ghostForLeg(c, leg);
1720}
1721
1722static naRef f_flightplan_insertWPAfter(naContext c, naRef me, int argc, naRef* args)
1723{
1724 FlightPlan* fp = flightplanGhost(me);
1725 if (!fp) {
1726 naRuntimeError(c, "flightplan.insertWPAfter called on non-flightplan object");
1727 }
1728
1729 WayptRef wp = wayptGhost(args[0]);
1730 int index = -1; // append
1731 if ((argc > 1) && naIsNum(args[1])) {
1732 index = (int)args[1].num;
1733 }
1734
1735 auto leg = fp->insertWayptAtIndex(wp.get(), index + 1);
1736 return ghostForLeg(c, leg);
1737}
1738
1739static naRef f_flightplan_insertWaypoints(naContext c, naRef me, int argc, naRef* args)
1740{
1741 FlightPlan* fp = flightplanGhost(me);
1742 if (!fp) {
1743 naRuntimeError(c, "flightplan.insertWaypoints called on non-flightplan object");
1744 }
1745
1746 // don't warn when passing a nil to this, which can happen in certain
1747 // procedure construction situations
1748 if (naIsNil(args[0])) {
1749 return naNil();
1750 }
1751
1752 WayptVec wps;
1753 if (!naIsVector(args[0])) {
1754 naRuntimeError(c, "flightplan.insertWaypoints expects vector as first arg");
1755 }
1756
1757 int count = naVec_size(args[0]);
1758 for (int i = 0; i < count; ++i) {
1759 Waypt* wp = wayptGhost(naVec_get(args[0], i));
1760 if (wp) {
1761 wps.push_back(wp);
1762 }
1763 }
1764
1765 int index = -1; // append
1766 if ((argc > 1) && naIsNum(args[1])) {
1767 index = (int)args[1].num;
1768 }
1769
1770 fp->insertWayptsAtIndex(wps, index);
1771 return naNil();
1772}
1773
1774static naRef f_flightplan_deleteWP(naContext c, naRef me, int argc, naRef* args)
1775{
1776 FlightPlan* fp = flightplanGhost(me);
1777 if (!fp) {
1778 naRuntimeError(c, "flightplan.deleteWP called on non-flightplan object");
1779 }
1780
1781 if ((argc < 1) || !naIsNum(args[0])) {
1782 naRuntimeError(c, "bad argument to flightplan.deleteWP");
1783 }
1784
1785 int index = (int)args[0].num;
1786 fp->deleteIndex(index);
1787 return naNil();
1788}
1789
1790static naRef f_flightplan_clearLegs(naContext c, naRef me, int argc, naRef* args)
1791{
1792 FlightPlan* fp = flightplanGhost(me);
1793 if (!fp) {
1794 naRuntimeError(c, "flightplan.clearLegs called on non-flightplan object");
1795 }
1796
1797 fp->clearLegs();
1798 return naNil();
1799}
1800
1801static naRef f_flightplan_clearAll(naContext c, naRef me, int argc, naRef* args)
1802{
1803 FlightPlan* fp = flightplanGhost(me);
1804 if (!fp) {
1805 naRuntimeError(c, "flightplan.clearAll called on non-flightplan object");
1806 }
1807
1808 fp->clearAll();
1809 return naNil();
1810}
1811
1812
1813static naRef f_flightplan_clearWPType(naContext c, naRef me, int argc, naRef* args)
1814{
1815 FlightPlan* fp = flightplanGhost(me);
1816 if (!fp) {
1817 naRuntimeError(c, "flightplan.clearWPType called on non-flightplan object");
1818 }
1819
1820 if (argc < 1) {
1821 naRuntimeError(c, "insufficent args to flightplan.clearWPType");
1822 }
1823
1824 WayptFlag flag = wayptFlagFromString(naStr_data(args[0]));
1825 if (flag == 0) {
1826 naRuntimeError(c, "clearWPType: bad waypoint role");
1827 }
1828
1829 fp->clearWayptsWithFlag(flag);
1830 return naNil();
1831}
1832
1833static naRef f_flightplan_clone(naContext c, naRef me, int argc, naRef* args)
1834{
1835 FlightPlan* fp = flightplanGhost(me);
1836 if (!fp) {
1837 naRuntimeError(c, "flightplan.clone called on non-flightplan object");
1838 }
1839
1840 const bool convertRouteToFP = (argc > 0) && naIsNum(args[0]) && (args[0].num != 0.0);
1841 return ghostForFlightPlan(c, fp->clone(fp->ident(), convertRouteToFP));
1842}
1843
1844static naRef f_flightplan_pathGeod(naContext c, naRef me, int argc, naRef* args)
1845{
1846 FlightPlan* fp = flightplanGhost(me);
1847 if (!fp) {
1848 naRuntimeError(c, "flightplan.pathGeod called on non-flightplan object");
1849 }
1850
1851 if ((argc < 1) || !naIsNum(args[0])) {
1852 naRuntimeError(c, "bad argument to flightplan.pathGeod");
1853 }
1854
1855 if ((argc > 1) && !naIsNum(args[1])) {
1856 naRuntimeError(c, "bad argument to flightplan.pathGeod");
1857 }
1858
1859 int index = (int)args[0].num;
1860 double offset = (argc > 1) ? args[1].num : 0.0;
1861 naRef result = naNewHash(c);
1862 SGGeod g = fp->pointAlongRoute(index, offset);
1863 hashset(c, result, "lat", naNum(g.getLatitudeDeg()));
1864 hashset(c, result, "lon", naNum(g.getLongitudeDeg()));
1865 return result;
1866}
1867
1868static naRef f_flightplan_finish(naContext c, naRef me, int argc, naRef* args)
1869{
1870 FlightPlan* fp = flightplanGhost(me);
1871 if (!fp) {
1872 naRuntimeError(c, "flightplan.finish called on non-flightplan object");
1873 }
1874
1875 // forbid on isRoute FPs?
1876
1877 fp->finish();
1878 return naNil();
1879}
1880
1881static naRef f_flightplan_activate(naContext c, naRef me, int argc, naRef* args)
1882{
1883 FlightPlan* fp = flightplanGhost(me);
1884 if (!fp) {
1885 naRuntimeError(c, "activate called on non-flightplan object");
1886 }
1887
1888 if (fp->isRoute()) {
1889 naRuntimeError(c, "activate called on isRoute flightplan");
1890 }
1891
1892 fp->activate();
1893 return naNil();
1894}
1895
1896
1897static naRef f_flightplan_indexOfWp(naContext c, naRef me, int argc, naRef* args)
1898{
1899 FlightPlan* fp = flightplanGhost(me);
1900 if (!fp) {
1901 naRuntimeError(c, "flightplan.indexOfWP called on non-flightplan object");
1902 }
1903
1904 FGPositioned* positioned = positionedGhost(args[0]);
1905 if (positioned) {
1906 return naNum(fp->findWayptIndex(positioned));
1907 }
1908
1909 FlightPlan::Leg* leg = fpLegGhost(args[0]);
1910 if (leg) {
1911 if (leg->owner() == fp) {
1912 return naNum(leg->index());
1913 }
1914
1915 naRuntimeError(c, "flightplan.indexOfWP called on leg from different flightplan");
1916 }
1917
1918 SGGeod pos;
1919 int argOffset = geodFromArgs(args, 0, argc, pos);
1920 if (argOffset > 0) {
1921 return naNum(fp->findWayptIndex(pos));
1922 }
1923
1924 return naNum(-1);
1925}
1926
1927static naRef f_flightplan_save(naContext c, naRef me, int argc, naRef* args)
1928{
1929 FlightPlan* fp = flightplanGhost(me);
1930 if (!fp) {
1931 naRuntimeError(c, "save called on non-flightplan object");
1932 }
1933
1934 if ((argc < 1) || !naIsString(args[0])) {
1935 naRuntimeError(c, "flightplan.save, no file path argument");
1936 }
1937
1938 const SGPath raw_path(naStr_data(args[0]));
1939 const SGPath validated_path = SGPath(raw_path).validate(true);
1940 if (validated_path.isNull()) {
1941 naRuntimeError(c, "flightplan.save, writing to path is not permitted");
1942 }
1943
1944 bool ok = fp->save(validated_path);
1945 return naNum(ok);
1946}
1947
1948static naRef f_flightplan_parseICAORoute(naContext c, naRef me, int argc, naRef* args)
1949{
1950 FlightPlan* fp = flightplanGhost(me);
1951 if (!fp) {
1952 naRuntimeError(c, "parseICAORoute called on non-flightplan object");
1953 }
1954
1955 if ((argc < 1) || !naIsString(args[0])) {
1956 naRuntimeError(c, "flightplan.parseICAORoute, no route argument");
1957 }
1958
1959 bool ok = fp->parseICAORouteString(naStr_data(args[0]));
1960 return naNum(ok);
1961}
1962
1963static naRef f_flightplan_toICAORoute(naContext c, naRef me, int, naRef*)
1964{
1965 FlightPlan* fp = flightplanGhost(me);
1966 if (!fp) {
1967 naRuntimeError(c, "toICAORoute called on non-flightplan object");
1968 }
1969
1970 return stringToNasal(c, fp->asICAORouteString());
1971}
1972
1973static naRef f_flightplan_computeDuration(naContext c, naRef me, int, naRef*)
1974{
1975 FlightPlan* fp = flightplanGhost(me);
1976 if (!fp) {
1977 naRuntimeError(c, "computeDuration called on non-flightplan object");
1978 }
1979
1981 return naNum(fp->estimatedDurationMinutes());
1982}
1983
1984static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
1985{
1986 FlightPlan::Leg* leg = fpLegGhost(me);
1987 if (!leg) {
1988 naRuntimeError(c, "leg.setSpeed called on non-flightplan-leg object");
1989 }
1990
1991 double speed = 0.0;
1993 RouteUnits units = DEFAULT_UNITS;
1994
1995 if (argc > 0) {
1996 if (naIsNil(args[0])) {
1997 // clear the restriction to NONE
1998 rr = RESTRICT_NONE;
1999 } else if (convertToNum(args[0], speed)) {
2000 if ((argc > 1) && naIsString(args[1])) {
2001 rr = routeRestrictionFromArg(args[1]);
2002 } else {
2003 naRuntimeError(c, "bad arguments to setSpeed");
2004 }
2005
2006 if (argc > 2) {
2007 units = routeUnitsFromArg(args[2]);
2008 }
2009 }
2010
2011 leg->setSpeed(rr, speed, units);
2012 } else {
2013 naRuntimeError(c, "bad arguments to setSpeed");
2014 }
2015
2016 return naNil();
2017}
2018
2019static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
2020{
2021 FlightPlan::Leg* leg = fpLegGhost(me);
2022 if (!leg) {
2023 naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object");
2024 }
2025
2026 double altitude = 0.0;
2028 RouteUnits units = DEFAULT_UNITS;
2029
2030 if (argc > 0) {
2031 if (naIsNil(args[0])) {
2032 // clear the restriction to NONE
2033 rr = RESTRICT_NONE;
2034 } else if (convertToNum(args[0], altitude)) {
2035 if (argc > 1) {
2036 rr = routeRestrictionFromArg(args[1]);
2037 } else {
2038 naRuntimeError(c, "bad arguments to leg.setAltitude");
2039 }
2040
2041 if (argc > 2) {
2042 units = routeUnitsFromArg(args[2]);
2043 }
2044 } else if (naIsVector(args[0])) {
2045 const auto altTuple = args[0];
2046 // we need a second restriction type arg, and the tuple should be of length 2
2047 if ((argc < 2) || (naVec_size(altTuple) != 2)) {
2048 naRuntimeError(c, "missing/bad arguments to leg.setAltitude");
2049 }
2050
2051 rr = routeRestrictionFromArg(args[1]);
2052 if (rr != RESTRICT_BETWEEN) {
2053 naRuntimeError(c, "leg.setAltitude: passed a 2-tuple, but restriction type is not 'between'");
2054 }
2055
2056 double constraintAltitude;
2057 const auto ok = convertToNum(naVec_get(altTuple, 0), constraintAltitude)
2058 && convertToNum(naVec_get(altTuple, 1), altitude);
2059 if (!ok) {
2060 naRuntimeError(c, "leg.setAltitude: tuple members not convertible to numeric altitudes");
2061 }
2062
2063 if (argc > 2) {
2064 units = routeUnitsFromArg(args[2]);
2065 }
2066
2067 // TODO: store constraint altitude
2068 }
2069
2070 leg->setAltitude(rr, altitude, units);
2071 } else {
2072 naRuntimeError(c, "bad arguments to setleg.setAltitude");
2073 }
2074
2075 return naNil();
2076}
2077
2078static naRef f_leg_altitude(naContext c, naRef me, int argc, naRef* args)
2079{
2080 FlightPlan::Leg* leg = fpLegGhost(me);
2081 if (!leg) {
2082 naRuntimeError(c, "leg.altitude called on non-flightplan-leg object");
2083 }
2084
2085 RouteUnits units = DEFAULT_UNITS;
2086 if (argc > 0) {
2087 units = routeUnitsFromArg(args[9]);
2088 }
2089
2090 if (leg->altitudeRestriction() == RESTRICT_BETWEEN) {
2091 naRef result = naNewVector(c);
2092 naVec_append(result, naNum(leg->waypoint()->constraintAltitude(units)));
2093 naVec_append(result, naNum(leg->altitude(units)));
2094 return result;
2095 }
2096
2097 return naNum(leg->altitude(units));
2098}
2099
2100static naRef f_leg_speed(naContext c, naRef me, int argc, naRef* args)
2101{
2102 FlightPlan::Leg* leg = fpLegGhost(me);
2103 if (!leg) {
2104 naRuntimeError(c, "leg.speed called on non-flightplan-leg object");
2105 }
2106
2107 RouteUnits units = DEFAULT_UNITS;
2108 if (argc > 0) {
2109 units = routeUnitsFromArg(args[9]);
2110 }
2111
2112 return naNum(leg->speed(units));
2113}
2114
2115static naRef f_leg_courseAndDistanceFrom(naContext c, naRef me, int argc, naRef* args)
2116{
2117 FlightPlan::Leg* leg = fpLegGhost(me);
2118 if (!leg || !leg->owner()) {
2119 naRuntimeError(c, "leg.courseAndDistanceFrom called on non-flightplan-leg object");
2120 }
2121
2122 SGGeod pos;
2123 geodFromArgs(args, 0, argc, pos);
2124
2125 RoutePath path(leg->owner());
2126 SGGeod wpPos = path.positionForIndex(leg->index());
2127 double courseDeg, az2, distanceM;
2128 SGGeodesy::inverse(pos, wpPos, courseDeg, az2, distanceM);
2129
2130 naRef result = naNewVector(c);
2131 naVec_append(result, naNum(courseDeg));
2132 naVec_append(result, naNum(distanceM * SG_METER_TO_NM));
2133 return result;
2134}
2135
2136static naRef f_leg_path(naContext c, naRef me, int argc, naRef* args)
2137{
2138 FlightPlan::Leg* leg = fpLegGhost(me);
2139 if (!leg || !leg->owner()) {
2140 naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object");
2141 }
2142
2143 RoutePath path(leg->owner());
2144 SGGeodVec gv(path.pathForIndex(leg->index()));
2145
2146 naRef result = naNewVector(c);
2147 for (SGGeod p : gv) {
2148 // construct a geo.Coord!
2149 naRef coord = naNewHash(c);
2150 hashset(c, coord, "lat", naNum(p.getLatitudeDeg()));
2151 hashset(c, coord, "lon", naNum(p.getLongitudeDeg()));
2152 naVec_append(result, coord);
2153 }
2154
2155 return result;
2156}
2157
2158static naRef f_procedure_transition(naContext c, naRef me, int argc, naRef* args)
2159{
2160 Procedure* proc = procedureGhost(me);
2161 if (!proc) {
2162 naRuntimeError(c, "procedure.transition called on non-procedure object");
2163 }
2164
2165 const std::string ident{naStr_data(args[0])};
2166 const auto ty = proc->type();
2167 if (Approach::isApproach(ty)) {
2168 const auto app = static_cast<Approach*>(proc);
2169 const auto trans = app->findTransitionByName(ident);
2170 return ghostForProcedure(c, trans);
2171 }
2172
2173 if ((ty != PROCEDURE_SID) && (ty != PROCEDURE_STAR)) {
2174 naRuntimeError(c, "procedure.transition called on non-SID or -STAR");
2175 }
2176
2178 Transition* trans = ad->findTransitionByName(ident);
2179
2180 return ghostForProcedure(c, trans);
2181}
2182
2183static naRef approachRoute(Approach* app, naContext c, int argc, naRef* args)
2184{
2185 int argOffset = 0;
2186 FGRunway* rwy = runwayGhost(args[0]);
2187 if (rwy) ++argOffset;
2188
2189 WayptVec r;
2190 Transition* trans = nullptr;
2191 WayptRef iaf;
2192
2193 if (argOffset < argc) {
2194 // one of these might work, never both
2195 trans = (Transition*)procedureGhost(args[argOffset]);
2196 iaf = wayptFromArg(args[argOffset]);
2197
2198 if (trans) {
2199 bool ok = app->routeWithTransition(rwy, trans, r);
2200 if (!ok)
2201 return naNil();
2202
2203 return convertWayptVecToNasal(c, r);
2204 }
2205 }
2206
2207 bool ok = app->route(rwy, iaf, r);
2208 if (!ok)
2209 return naNil();
2210
2211 return convertWayptVecToNasal(c, r);
2212}
2213
2214static naRef f_procedure_route(naContext c, naRef me, int argc, naRef* args)
2215{
2216 Procedure* proc = procedureGhost(me);
2217 if (!proc) {
2218 naRuntimeError(c, "procedure.route called on non-procedure object");
2219 }
2220
2221 // wrapping up tow different routines here - approach routing
2222 // to the associated runway, and SID/STAR routing via an enroute transition
2223 // and possibly a runway transition or not.
2224 if (Approach::isApproach(proc->type())) {
2225 return approachRoute(static_cast<Approach*>(proc), c, argc, args);
2226 } else if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) {
2227 naRuntimeError(c, "procedure.route called on unsuitable procedure type");
2228 }
2229
2230 int argOffset = 0;
2231 FGRunway* rwy = runwayGhost(args[0]);
2232 if (rwy) ++argOffset;
2233
2235 Transition* trans = NULL;
2236 if (argOffset < argc) {
2237 trans = (Transition*)procedureGhost(args[argOffset]);
2238 }
2239
2240 // note either runway or trans may be NULL - that's ok
2241 WayptVec r;
2242 if (!ad->route(rwy, trans, r)) {
2243 SG_LOG(SG_NASAL, SG_WARN, "procedure.route failed for ArrivalDeparture somehow");
2244 return naNil();
2245 }
2246
2247 return convertWayptVecToNasal(c, r);
2248}
2249
2250static naRef f_airway_contains(naContext c, naRef me, int argc, naRef* args)
2251{
2252 Airway* awy = airwayGhost(me);
2253 if (!awy) {
2254 naRuntimeError(c, "airway.contains called on non-airway object");
2255 }
2256
2257 if (argc < 1) {
2258 naRuntimeError(c, "missing arg to airway.contains");
2259 }
2260
2261 auto pos = positionedFromArg(args[0]);
2262 if (!pos) {
2263 return naNum(0);
2264 }
2265
2266 return naNum(awy->containsNavaid(pos));
2267}
2268
2269static naRef f_airway_viaWaypoints(naContext c, naRef me, int argc, naRef* args)
2270{
2271 Airway* awy = airwayGhost(me);
2272 if (!awy) {
2273 naRuntimeError(c, "airway.viaWaypoints called on non-airway object");
2274 }
2275
2276 if ((argc < 1) || (argc > 2)) {
2277 naRuntimeError(c, "Airway.viaWaypoints: needs one ro two arguments");
2278 }
2279
2280 auto from = positionedFromArg(args[0]);
2281 auto fromWp = awy->findEnroute(from);
2282 if (!fromWp) {
2283 naRuntimeError(c, "Airway.viaWaypoints: from wp not found");
2284 }
2285
2286 naRef toArg = args[1];
2287 FGPositionedRef nav;
2288 if (naIsString(toArg)) {
2289 WayptRef enroute = awy->findEnroute(naStr_data(toArg));
2290 if (!enroute) {
2291 naRuntimeError(c, "unknown waypoint on airway %s: %s",
2292 awy->ident().c_str(), naStr_data(toArg));
2293 }
2294
2295 nav = enroute->source();
2296 } else {
2297 nav = positionedFromArg(toArg);
2298 if (!nav) {
2299 naRuntimeError(c, "Airway.viaWaypoints: final arg is not a navaid");
2300 }
2301 }
2302
2303 auto toWaypt = awy->findEnroute(nav);
2304 if (!toWaypt) {
2305 naRuntimeError(c, "Airway.viaWaypoints: navaid not on airway");
2306 }
2307
2308 auto wps = awy->via(fromWp, toWaypt);
2309 return convertWayptVecToNasal(c, wps);
2310}
2311
2312// Table of extension functions. Terminate with zeros.
2313static struct {
2314 const char* name;
2315 naCFunction func;
2316} funcs[] = {
2317 {"flightplan", f_flightplan},
2318 {"createFlightplan", f_createFlightplan},
2319 {"registerFlightPlanDelegate", f_registerFPDelegate},
2320 {"unregisterFlightPlanDelegate", f_unregisterFPDelegate},
2321 {"createWP", f_createWP},
2322 {"createWPFrom", f_createWPFrom},
2323 {"createViaTo", f_createViaTo},
2324 {"createViaFromTo", f_createViaFromTo},
2325 {"createDiscontinuity", f_createDiscontinuity},
2326 {"airwaysRoute", f_airwaySearch},
2327 {"airway", f_findAirway},
2328 {nullptr, nullptr}};
2329
2330
2331naRef initNasalFlightPlan(naRef globals, naContext c)
2332{
2333 flightplanPrototype = naNewHash(c);
2334 naSave(c, flightplanPrototype);
2335
2336 hashset(c, flightplanPrototype, "getWP", naNewFunc(c, naNewCCode(c, f_flightplan_getWP)));
2337 hashset(c, flightplanPrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_flightplan_currentWP)));
2338 hashset(c, flightplanPrototype, "nextWP", naNewFunc(c, naNewCCode(c, f_flightplan_nextWP)));
2339 hashset(c, flightplanPrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_flightplan_numWaypoints)));
2340 // alias to this name also
2341 hashset(c, flightplanPrototype, "numWaypoints", naNewFunc(c, naNewCCode(c, f_flightplan_numWaypoints)));
2342 hashset(c, flightplanPrototype, "numRemainingWaypoints", naNewFunc(c, naNewCCode(c, f_flightplan_numRemainingWaypoints)));
2343
2344 hashset(c, flightplanPrototype, "appendWP", naNewFunc(c, naNewCCode(c, f_flightplan_appendWP)));
2345 hashset(c, flightplanPrototype, "insertWP", naNewFunc(c, naNewCCode(c, f_flightplan_insertWP)));
2346 hashset(c, flightplanPrototype, "deleteWP", naNewFunc(c, naNewCCode(c, f_flightplan_deleteWP)));
2347 hashset(c, flightplanPrototype, "insertWPAfter", naNewFunc(c, naNewCCode(c, f_flightplan_insertWPAfter)));
2348 hashset(c, flightplanPrototype, "insertWaypoints", naNewFunc(c, naNewCCode(c, f_flightplan_insertWaypoints)));
2349
2350 auto f = naNewFunc(c, naNewCCode(c, f_flightplan_clearLegs));
2351 hashset(c, flightplanPrototype, "cleanPlan", f); // original name for compat
2352 // alias to a better name
2353 hashset(c, flightplanPrototype, "clearLegs", f);
2354
2355
2356 hashset(c, flightplanPrototype, "clearAll", naNewFunc(c, naNewCCode(c, f_flightplan_clearAll)));
2357
2358 hashset(c, flightplanPrototype, "clearWPType", naNewFunc(c, naNewCCode(c, f_flightplan_clearWPType)));
2359 hashset(c, flightplanPrototype, "clone", naNewFunc(c, naNewCCode(c, f_flightplan_clone)));
2360
2361 hashset(c, flightplanPrototype, "pathGeod", naNewFunc(c, naNewCCode(c, f_flightplan_pathGeod)));
2362 // this is a clearer name than pathGeod
2363 hashset(c, flightplanPrototype, "pointAlongRoute", naNewFunc(c, naNewCCode(c, f_flightplan_pathGeod)));
2364
2365 hashset(c, flightplanPrototype, "finish", naNewFunc(c, naNewCCode(c, f_flightplan_finish)));
2366 hashset(c, flightplanPrototype, "activate", naNewFunc(c, naNewCCode(c, f_flightplan_activate)));
2367 hashset(c, flightplanPrototype, "indexOfWP", naNewFunc(c, naNewCCode(c, f_flightplan_indexOfWp)));
2368 hashset(c, flightplanPrototype, "computeDuration", naNewFunc(c, naNewCCode(c, f_flightplan_computeDuration)));
2369 hashset(c, flightplanPrototype, "parseICAORoute", naNewFunc(c, naNewCCode(c, f_flightplan_parseICAORoute)));
2370 hashset(c, flightplanPrototype, "toICAORoute", naNewFunc(c, naNewCCode(c, f_flightplan_toICAORoute)));
2371
2372 hashset(c, flightplanPrototype, "save", naNewFunc(c, naNewCCode(c, f_flightplan_save)));
2373
2374 procedurePrototype = naNewHash(c);
2375 naSave(c, procedurePrototype);
2376 hashset(c, procedurePrototype, "transition", naNewFunc(c, naNewCCode(c, f_procedure_transition)));
2377 hashset(c, procedurePrototype, "route", naNewFunc(c, naNewCCode(c, f_procedure_route)));
2378
2379 fpLegPrototype = naNewHash(c);
2380 naSave(c, fpLegPrototype);
2381 hashset(c, fpLegPrototype, "setSpeed", naNewFunc(c, naNewCCode(c, f_leg_setSpeed)));
2382 hashset(c, fpLegPrototype, "setAltitude", naNewFunc(c, naNewCCode(c, f_leg_setAltitude)));
2383 hashset(c, fpLegPrototype, "altitude", naNewFunc(c, naNewCCode(c, f_leg_altitude)));
2384 hashset(c, fpLegPrototype, "speed", naNewFunc(c, naNewCCode(c, f_leg_speed)));
2385 hashset(c, fpLegPrototype, "path", naNewFunc(c, naNewCCode(c, f_leg_path)));
2386 hashset(c, fpLegPrototype, "courseAndDistanceFrom", naNewFunc(c, naNewCCode(c, f_leg_courseAndDistanceFrom)));
2387
2388 airwayPrototype = naNewHash(c);
2389 naSave(c, airwayPrototype);
2390 hashset(c, airwayPrototype, "contains", naNewFunc(c, naNewCCode(c, f_airway_contains)));
2391 hashset(c, airwayPrototype, "viaWaypoints", naNewFunc(c, naNewCCode(c, f_airway_viaWaypoints)));
2392 hashset(c, airwayPrototype, "HIGH", naNum(Airway::HighLevel));
2393 hashset(c, airwayPrototype, "LOW", naNum(Airway::LowLevel));
2394 hashset(c, airwayPrototype, "BOTH", naNum(Airway::Both));
2395
2396 hashset(c, globals, "Airway", airwayPrototype);
2397
2398 for (int i = 0; funcs[i].name; i++) {
2399 hashset(c, globals, funcs[i].name,
2400 naNewFunc(c, naNewCCode(c, funcs[i].func)));
2401 }
2402
2403 return naNil();
2404}
double altitude
Definition ADA.cxx:46
#define p(x)
std::vector< SGGeod > SGGeodVec
static naRef approachRoute(Approach *app, naContext c, int argc, naRef *args)
static naRef stringToNasal(naContext c, const std::string &s)
naRef ghostForFlightPlan(naContext c, const FlightPlan *fp)
static naGhostType ProcedureGhostType
static naRef f_leg_path(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_toICAORoute(naContext c, naRef me, int, naRef *)
static naRef fpLegPrototype
static naRef f_createDiscontinuity(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_computeDuration(naContext c, naRef me, int, naRef *)
static void waypointGhostSetMember(naContext c, void *g, naRef field, naRef value)
static naRef procedurePrototype
static naRef f_flightplan_parseICAORoute(naContext c, naRef me, int argc, naRef *args)
static Airway * airwayGhost(naRef r)
static void hashset(naContext c, naRef hash, const char *key, naRef val)
static void flightplanGhostSetMember(naContext c, void *g, naRef field, naRef value)
static naRef f_createWP(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_getWP(naContext c, naRef me, int argc, naRef *args)
static naRef f_fpLeg_runway(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan(naContext c, naRef me, int argc, naRef *args)
static naRef f_airwaySearch(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_save(naContext c, naRef me, int argc, naRef *args)
static naRef f_procedure_route(naContext c, naRef me, int argc, naRef *args)
static struct @032150236342374176343243244364035346052374146336 funcs[]
static naRef f_leg_speed(naContext c, naRef me, int argc, naRef *args)
naRef ghostForWaypt(naContext c, const Waypt *wpt)
static void wayptGhostDestroy(void *g)
naRef ghostForAirway(naContext c, const Airway *awy)
static naGhostType FlightPlanGhostType
static naRef convertWayptVecToNasal(naContext c, const WayptVec &wps)
static naRef f_flightplan_insertWPAfter(naContext c, naRef me, int argc, naRef *args)
static const char * procedureGhostGetMember(naContext c, void *g, naRef field, naRef *out)
static naRef f_flightplan_clearAll(naContext c, naRef me, int argc, naRef *args)
static RouteUnits routeUnitsFromArg(naRef arg)
static void legGhostSetMember(naContext c, void *g, naRef field, naRef value)
static naRef f_flightplan_numWaypoints(naContext c, naRef me, int argc, naRef *args)
static const char * legGhostGetMember(naContext c, void *g, naRef field, naRef *out)
static naGhostType FPLegGhostType
static naGhostType WayptGhostType
static void legGhostDestroy(void *g)
static void routeBaseGhostDestroy(void *g)
static naRef f_airway_contains(naContext c, naRef me, int argc, naRef *args)
static const char * waypointCommonGetMember(naContext c, Waypt *wpt, const char *fieldName, naRef *out)
static naRef f_flightplan_clearWPType(naContext c, naRef me, int argc, naRef *args)
static naRef procedureTpType(naContext c, ProcedureType ty)
static WayptRef wayptFromArg(naRef arg)
static naRef f_createWPFrom(naContext c, naRef me, int argc, naRef *args)
static WayptFlag wayptFlagFromString(const char *s)
static naRef f_flightplan_deleteWP(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_appendWP(naContext c, naRef me, int argc, naRef *args)
static naRef f_leg_courseAndDistanceFrom(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_insertWaypoints(naContext c, naRef me, int argc, naRef *args)
static const char * wayptGhostGetMember(naContext c, void *g, naRef field, naRef *out)
static bool convertToNum(naRef v, double &result)
FlightPlan::Leg * fpLegGhost(naRef r)
static std::optional< Airway::Level > airwayLevelFromNasal(naRef na)
naRef ghostForLeg(naContext c, const FlightPlan::Leg *leg)
static naRef waypointNavaid(naContext c, Waypt *wpt)
static naRef f_flightplan_indexOfWp(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_nextWP(naContext c, naRef me, int argc, naRef *args)
static naRef f_registerFPDelegate(naContext c, naRef me, int argc, naRef *args)
naRef ghostForProcedure(naContext c, const Procedure *proc)
static naRef f_flightplan_currentWP(naContext c, naRef me, int argc, naRef *args)
static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef *args)
static const char * airwayGhostGetMember(naContext c, void *g, naRef field, naRef *out)
static naRef f_flightplan_insertWP(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_clearLegs(naContext c, naRef me, int argc, naRef *args)
static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef *args)
static naRef procedureRadioType(naContext c, ProcedureType ty)
static naRef waypointAirport(naContext c, Waypt *wpt)
static naRef flightplanPrototype
static naRef f_createViaFromTo(naContext c, naRef me, int argc, naRef *args)
static naRef f_airway_viaWaypoints(naContext c, naRef me, int argc, naRef *args)
static naRef f_findAirway(naContext c, naRef me, int argc, naRef *args)
naRef initNasalFlightPlan(naRef globals, naContext c)
void shutdownNasalFlightPlan()
static RouteRestriction routeRestrictionFromArg(naRef arg)
static naRef waypointRunway(naContext c, Waypt *wpt)
static naRef f_flightplan_numRemainingWaypoints(naContext c, naRef me, int argc, naRef *args)
static FlightPlan * flightplanGhost(naRef r)
static naRef airwayPrototype
static naRef f_createViaTo(naContext c, naRef me, int argc, naRef *args)
static naGhostType AirwayGhostType
static naRef f_flightplan_clone(naContext c, naRef me, int argc, naRef *args)
static naRef f_leg_altitude(naContext c, naRef me, int argc, naRef *args)
static const char * flightplanGhostGetMember(naContext c, void *g, naRef field, naRef *out)
naRef routeRestrictionToNasal(naContext c, RouteRestriction rr)
static std::vector< FlightPlan::DelegateFactoryRef > static_nasalDelegateFactories
static naRef f_flightplan_activate(naContext c, naRef me, int argc, naRef *args)
static naRef f_fpLeg_airport(naContext c, naRef me, int argc, naRef *args)
static naRef f_flightplan_finish(naContext c, naRef me, int argc, naRef *args)
static naRef f_fpLeg_navaid(naContext c, naRef me, int argc, naRef *args)
static naRef f_procedure_transition(naContext c, naRef me, int argc, naRef *args)
Procedure * procedureGhost(naRef r)
static naRef f_unregisterFPDelegate(naContext c, naRef me, int argc, naRef *args)
static naRef f_createFlightplan(naContext c, naRef me, int argc, naRef *args)
Waypt * wayptGhost(naRef r)
static bool waypointCommonSetMember(naContext c, Waypt *wpt, const char *fieldName, naRef value)
static naRef wayptFlagToNasal(naContext c, unsigned int flags)
static naRef f_flightplan_pathGeod(naContext c, naRef me, int argc, naRef *args)
FGAirport * airportGhost(naRef r)
FGRunway * runwayGhost(naRef r)
naRef ghostForRunway(naContext c, const FGRunway *r)
int geodFromArgs(naRef *args, int offset, int argc, SGGeod &result)
FGPositionedRef positionedFromArg(naRef ref)
naRef ghostForPositioned(naContext c, FGPositionedRef pos)
FGPositioned * positionedGhost(naRef r)
naRef ghostForAirport(naContext c, const FGAirport *apt)
#define i(x)
SGSharedPtr< FGRunway > FGRunwayRef
flightgear::STAR * findSTARWithIdent(const std::string &aIdent) const
Definition airport.cxx:921
flightgear::Approach * findApproachWithIdent(const std::string &aIdent) const
Definition airport.cxx:954
flightgear::SID * findSIDWithIdent(const std::string &aIdent) const
Definition airport.cxx:887
naRef callMethod(naRef code, naRef self, int argc, naRef *args, naRef locals)
Definition NasalSys.cxx:331
int gcSave(naRef r)
static bool isRunwayType(FGPositioned *pos)
static bool isAirportType(FGPositioned *pos)
static bool isNavaidType(FGPositioned *pos)
Type type() const
const std::string & ident() const
Top level route manager class.
Definition route_mgr.hxx:27
void destroyFlightPlanDelegate(FlightPlan *fp, FlightPlan::Delegate *d) override
FlightPlan::Delegate * createFlightPlanDelegate(FlightPlan *fp) override
const std::string & id() const
NasalFPDelegateFactory(naRef code, const std::string &id)
void cleared() override
void currentWaypointChanged() override
void arrivalChanged() override
void sequence() override
Invoked when the C++ code determines the active leg is done / next leg should be sequenced.
void activated() override
void loaded() override
~NasalFPDelegate() override
void endOfFlightPlan() override
void waypointsChanged() override
void departureChanged() override
NasalFPDelegate(FlightPlan *fp, FGNasalSys *sys, naRef ins)
flightgear::SGGeodVec pathForIndex(int index) const
SGGeod positionForIndex(int index) const
bool route(WayptRef aFrom, WayptRef aTo, WayptVec &aPath)
Principal routing algorithm.
Definition airways.cxx:457
static Network * lowLevel()
Definition airways.cxx:93
WayptVec via(const WayptRef &from, const WayptRef &to) const
Definition airways.cxx:229
@ HighLevel
Victor airways.
Definition airways.hxx:48
@ Both
Jet airways.
Definition airways.hxx:49
bool containsNavaid(const FGPositionedRef &navaid) const
Definition airways.cxx:266
WayptRef findEnroute(const std::string &aIdent) const
Definition airways.cxx:386
static AirwayRef findByIdentAndNavaid(const std::string &aIdent, const FGPositionedRef nav)
Find an airway by ident, and containing a particula rnavaid/fix.
Definition airways.cxx:371
static Network * highLevel()
Definition airways.cxx:105
std::string ident() const override
Definition airways.hxx:52
static AirwayRef findByIdent(const std::string &aIdent, Level level)
Definition airways.cxx:294
Level level() const
Definition airways.hxx:58
Describe an approach procedure, including the missed approach segment.
Transition * findTransitionByName(const std::string &aIdent) const
static bool isApproach(ProcedureType ty)
bool routeWithTransition(FGRunwayRef runway, Transition *trans, WayptVec &aWps)
bool route(FGRunwayRef runway, WayptRef aIAF, WayptVec &aWps)
Build a route from a valid IAF to the runway, including the missed segment.
virtual bool route(FGRunwayRef aWay, Transition *trans, WayptVec &aPath)=0
Find a path between the runway and enroute structure.
string_list transitionIdents() const
Transition * findTransitionByName(const std::string &aIdent) const
Find an enroute transition waypoint by identifier.
Represent a route discontinuity.
Definition waypoint.hxx:325
abstract interface for creating delegates automatically when a flight-plan is created or loaded
flight-plan leg encapsulation
double speed(RouteUnits units=DEFAULT_UNITS) const
double distanceAlongRoute() const
RouteRestriction altitudeRestriction() const
void setSpeed(RouteRestriction ty, double speed, RouteUnits units=DEFAULT_UNITS)
RouteRestriction speedRestriction() const
bool setHoldCount(int count)
requesting holding at the waypoint upon reaching it.
void markWaypointDirty()
helper function, if the waypoint is modified in some way, to notify the flightplan owning this leg,...
double altitude(RouteUnits units=DEFAULT_UNITS) const
unsigned int index() const
FlightPlan * owner() const
void setAltitude(RouteRestriction ty, double alt, RouteUnits units=DEFAULT_UNITS)
void setCruiseSpeedMach(double mach)
std::string icaoAircraftCategory() const
void deleteIndex(int index)
Approach * approach() const
std::shared_ptr< DelegateFactory > DelegateFactoryRef
FGAirportRef departureAirport() const
int indexOfFirstNonDepartureWaypoint() const
int indexOfFirstApproachWaypoint() const
std::string remarks() const
LegRef nextLeg() const
int clearWayptsWithFlag(WayptFlag flag)
bool parseICAORouteString(const std::string &routeData)
attempt to replace the route waypoints (and potentially the SID and STAR) based on an ICAO standard r...
void setSID(SID *sid, const std::string &transition=std::string())
int indexOfFirstArrivalWaypoint() const
void insertWayptsAtIndex(const WayptVec &wps, int aIndex)
FlightPlanRef clone(const std::string &newIdent={}, bool convertToFlightPlan=false) const
void setCruiseSpeedKPH(int kmh)
static void unregisterDelegateFactory(DelegateFactoryRef df)
void setSTAR(STAR *star, const std::string &transition=std::string())
std::string callsign() const
bool isRoute() const
is this flight-pan a route (for planning) or an active flight-plan (which can be flown?...
static FlightPlanRef create()
create a FlightPlan with isRoute not set
void setEstimatedDurationMinutes(int minutes)
void setCruiseFlightLevel(int flightLevel)
void setDeparture(FGAirport *apt)
double totalDistanceNm() const
FGRunway * destinationRunway() const
std::string asICAORouteString() const
void setCruiseAltitudeFt(int altFt)
double cruiseSpeedMach() const
LegRef insertWayptAtIndex(Waypt *aWpt, int aIndex)
FGRunway * departureRunway() const
void setRemarks(const std::string &remarks)
void setApproach(Approach *app, const std::string &transition={})
note setting an approach will implicitly update the destination airport and runway to match
int estimatedDurationMinutes() const
int cruiseFlightLevel() const
bool followLegTrackToFixes() const
void setDestination(FGAirport *apt)
void setCallsign(const std::string &callsign)
Transition * approachTransition() const
virtual std::string ident() const
int findWayptIndex(const SGGeod &aPos) const
void setIdent(const std::string &s)
void setCurrentIndex(int index)
LegRef legAtIndex(int index) const
void setCruiseSpeedKnots(int kts)
void setFollowLegTrackToFixes(bool tf)
FGAirportRef destinationAirport() const
bool save(const SGPath &p) const
static void registerDelegateFactory(DelegateFactoryRef df)
Transition * sidTransition() const
SGGeod pointAlongRoute(int aIndex, double aOffsetNm) const
given a waypoint index, and an offset in NM, find the geodetic position on the route path.
void setIcaoAircraftCategory(const std::string &cat)
int indexOfDestinationRunwayWaypoint() const
void computeDurationMinutes()
computeDurationMinutes - use performance data and cruise data to estimate enroute time
static FlightPlanRef createRoute()
@factory to create a FlightPlan with isRoute=true
void setCruiseAltitudeM(int altM)
Transition * starTransition() const
Waypoint based upon a navaid.
Definition waypoint.hxx:63
virtual ProcedureType type() const =0
virtual RunwayVec runways() const
Definition procedure.hxx:58
virtual std::string ident() const
Definition procedure.hxx:53
virtual FGAirport * airport() const =0
virtual std::string ident() const =0
Waypoint based upon a runway.
Definition waypoint.hxx:117
Encapsulate a transition segment.
Definition procedure.hxx:70
Abstract base class for waypoints (and things that are treated similarly by navigation systems).
Definition route.hxx:105
virtual unsigned int flags() const
Definition route.hxx:156
RouteBase * owner() const
Definition route.hxx:109
virtual FGPositioned * source() const
The Positioned associated with this element, if one exists.
Definition route.hxx:117
virtual std::string type() const =0
void setFlag(WayptFlag aFlag, bool aV=true)
Definition route.cxx:209
virtual bool flag(WayptFlag aFlag) const
Test if the specified flag is set for this element.
Definition route.cxx:204
virtual double headingRadialDeg() const
return the assoicated heading or radial for this waypoint.
Definition route.cxx:337
virtual std::string ident() const
Identifier assoicated with the waypoint.
Definition route.cxx:199
virtual SGGeod position() const =0
double constraintAltitude(RouteUnits aUnits=DEFAULT_UNITS) const
Definition route.cxx:312
const char * name
FGGlobals * globals
Definition globals.cxx:142
std::vector< std::string > string_list
Definition globals.hxx:36
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
SGSharedPtr< FlightPlan > FlightPlanRef
@ SPEED_KPH
Definition route.hxx:89
@ ALTITUDE_FLIGHTLEVEL
Definition route.hxx:86
@ DEFAULT_UNITS
Definition route.hxx:83
@ ALTITUDE_METER
Definition route.hxx:85
@ SPEED_KNOTS
Definition route.hxx:87
@ SPEED_MACH
Definition route.hxx:88
@ ALTITUDE_FEET
Definition route.hxx:84
SGSharedPtr< FGPositioned > FGPositionedRef
Definition airways.cxx:49
SGSharedPtr< Waypt > WayptRef
SGSharedPtr< Airway > AirwayRef
Definition airways.hxx:40
bool isMachRestrict(RouteRestriction rr)
Definition route.cxx:59
@ WPT_DEPARTURE
Definition route.hxx:54
@ WPT_PSEUDO
waypoint generated by VNAV / speed management profile, for step climbs or top of descent
Definition route.hxx:59
@ WPT_MISS
segment is part of missed approach
Definition route.hxx:46
@ WPT_HIDDEN
waypoint should not be shown in UI displays, etc this is used to implement FMSs which delete waypoint...
Definition route.hxx:67
@ WPT_APPROACH
Definition route.hxx:60
@ WPT_ARRIVAL
Definition route.hxx:55
@ WPT_OVERFLIGHT
must overfly the point directly
Definition route.hxx:44
std::vector< WayptRef > WayptVec
RouteRestriction
Definition route.hxx:70
@ RESTRICT_NONE
Definition route.hxx:71
@ RESTRICT_AT
Definition route.hxx:72
@ RESTRICT_BELOW
Definition route.hxx:74
@ RESTRICT_DELETE
ignore underlying restriction (on a leg)
Definition route.hxx:77
@ RESTRICT_COMPUTED
data is computed, not a real restriction
Definition route.hxx:78
@ SPEED_COMPUTED_MACH
variant on above to encode a Mach value
Definition route.hxx:79
@ RESTRICT_ABOVE
Definition route.hxx:73
@ SPEED_RESTRICT_MACH
encode an 'AT' restriction in Mach, not IAS
Definition route.hxx:76
@ RESTRICT_BETWEEN
Definition route.hxx:75
@ PROCEDURE_TRANSITION
Definition procedure.hxx:44
@ PROCEDURE_APPROACH_NDB
Definition procedure.hxx:40
@ PROCEDURE_APPROACH_VOR
Definition procedure.hxx:39
@ PROCEDURE_RUNWAY_TRANSITION
Definition procedure.hxx:45
@ PROCEDURE_APPROACH_RNAV
Definition procedure.hxx:41
@ PROCEDURE_APPROACH_ILS
Definition procedure.hxx:38
naCFunction func
T * fgpositioned_cast(FGPositioned *p)