]> git.mxchange.org Git - flightgear.git/blob - src/Scripting/NasalPositioned_cppbind.cxx
8d51624a88ea46daed72e174daca23e53841fb9c
[flightgear.git] / src / Scripting / NasalPositioned_cppbind.cxx
1 // NasalPositioned_cppbind.cxx -- expose FGPositioned classes to Nasal
2 //
3 // Port of NasalPositioned.cpp to the new nasal/cppbind helpers. Will replace
4 // old NasalPositioned.cpp once finished.
5 //
6 // Copyright (C) 2013  Thomas Geymayer <tomgey@gmail.com>
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include "NasalPositioned.hxx"
27
28 #include <algorithm>
29 #include <functional>
30
31 #include <boost/foreach.hpp>
32 #include <boost/algorithm/string/case_conv.hpp>
33
34 #include <simgear/nasal/cppbind/from_nasal.hxx>
35 #include <simgear/nasal/cppbind/to_nasal.hxx>
36 #include <simgear/nasal/cppbind/NasalHash.hxx>
37 #include <simgear/nasal/cppbind/Ghost.hxx>
38
39 #include <Airports/airport.hxx>
40 #include <Airports/dynamics.hxx>
41 #include <Airports/pavement.hxx>
42 #include <ATC/CommStation.hxx>
43 #include <Main/globals.hxx>
44 #include <Navaids/NavDataCache.hxx>
45 #include <Navaids/navlist.hxx>
46 #include <Navaids/navrecord.hxx>
47 #include <Navaids/fix.hxx>
48
49 typedef nasal::Ghost<FGPositionedRef> NasalPositioned;
50 typedef nasal::Ghost<FGRunwayRef> NasalRunway;
51 typedef nasal::Ghost<FGParkingRef> NasalParking;
52 typedef nasal::Ghost<FGAirportRef> NasalAirport;
53 typedef nasal::Ghost<flightgear::CommStationRef> NasalCommStation;
54 typedef nasal::Ghost<FGNavRecordRef> NasalNavRecord;
55 typedef nasal::Ghost<FGRunwayRef> NasalRunway;
56 typedef nasal::Ghost<FGFixRef> NasalFix;
57
58 //------------------------------------------------------------------------------
59 naRef to_nasal_helper(naContext c, flightgear::SID* sid)
60 {
61   // TODO SID ghost
62   return nasal::to_nasal(c, sid->ident());
63 }
64
65 //------------------------------------------------------------------------------
66 naRef to_nasal_helper(naContext c, flightgear::STAR* star)
67 {
68   // TODO STAR ghost
69   return nasal::to_nasal(c, star->ident());
70 }
71
72 //------------------------------------------------------------------------------
73 naRef to_nasal_helper(naContext c, flightgear::Approach* iap)
74 {
75   // TODO Approach ghost
76   return nasal::to_nasal(c, iap->ident());
77 }
78
79 //------------------------------------------------------------------------------
80 static naRef f_navaid_course(naContext, FGNavRecord& nav)
81 {
82   if( !(  nav.type() == FGPositioned::ILS
83        || nav.type() == FGPositioned::LOC
84        ) )
85     return naNil();
86
87   double radial = nav.get_multiuse();
88   return naNum(SGMiscd::normalizePeriodic(0.5, 360.5, radial));
89 }
90
91 //------------------------------------------------------------------------------
92 static FGRunwayBaseRef f_airport_runway(FGAirport& apt, std::string ident)
93 {
94   boost::to_upper(ident);
95
96   if( apt.hasRunwayWithIdent(ident) )
97     return apt.getRunwayByIdent(ident);
98   else if( apt.hasHelipadWithIdent(ident) )
99     return apt.getHelipadByIdent(ident);
100
101   return 0;
102 }
103
104 //------------------------------------------------------------------------------
105 template<class T, class C1, class C2>
106 std::vector<T> extract( const std::vector<C1>& in,
107                         T (C2::*getter)() const )
108 {
109   std::vector<T> ret(in.size());
110   std::transform(in.begin(), in.end(), ret.begin(), std::mem_fun(getter));
111   return ret;
112 }
113
114 //------------------------------------------------------------------------------
115 static naRef f_airport_comms(FGAirport& apt, const nasal::CallContext& ctx)
116 {
117   FGPositioned::Type comm_type =
118     FGPositioned::typeFromName( ctx.getArg<std::string>(0) );
119
120   // if we have an explicit type, return a simple vector of frequencies
121   if( comm_type != FGPositioned::INVALID )
122     return ctx.to_nasal
123     (
124       extract( apt.commStationsOfType(comm_type),
125                &flightgear::CommStation::freqMHz )
126     );
127   else
128     // otherwise return a vector of ghosts, one for each comm station.
129     return ctx.to_nasal(apt.commStations());
130 }
131
132 //------------------------------------------------------------------------------
133 FGRunway* runwayFromNasalArg( const FGAirport& apt,
134                               const nasal::CallContext& ctx,
135                               size_t index = 0 )
136 {
137   if( index >= ctx.argc )
138     return NULL;
139
140   try
141   {
142     std::string ident = ctx.getArg<std::string>(index);
143     if( !ident.empty() )
144     {
145       if( !apt.hasRunwayWithIdent(ident) )
146         // TODO warning/exception?
147         return NULL;
148
149       return apt.getRunwayByIdent(ident);
150     }
151   }
152   catch(...)
153   {}
154
155   // TODO warn/error if no runway?
156   return NasalRunway::fromNasal(ctx.c, ctx.args[index]);
157 }
158
159 //------------------------------------------------------------------------------
160 static naRef f_airport_sids(FGAirport& apt, const nasal::CallContext& ctx)
161 {
162   FGRunway* rwy = runwayFromNasalArg(apt, ctx);
163   return ctx.to_nasal
164   (
165     extract(rwy ? rwy->getSIDs() : apt.getSIDs(), &flightgear::SID::ident)
166   );
167 }
168
169 //------------------------------------------------------------------------------
170 static naRef f_airport_stars(FGAirport& apt, const nasal::CallContext& ctx)
171 {
172   FGRunway* rwy = runwayFromNasalArg(apt, ctx);
173   return ctx.to_nasal
174   (
175     extract(rwy ? rwy->getSTARs() : apt.getSTARs(), &flightgear::STAR::ident)
176   );
177 }
178
179 //------------------------------------------------------------------------------
180 static naRef f_airport_approaches(FGAirport& apt, const nasal::CallContext& ctx)
181 {
182   FGRunway* rwy = runwayFromNasalArg(apt, ctx);
183
184   flightgear::ProcedureType type = flightgear::PROCEDURE_INVALID;
185   std::string type_str = ctx.getArg<std::string>(1);
186   if( !type_str.empty() )
187   {
188     boost::to_upper(type_str);
189     if(      type_str == "NDB" ) type = flightgear::PROCEDURE_APPROACH_NDB;
190     else if( type_str == "VOR" ) type = flightgear::PROCEDURE_APPROACH_VOR;
191     else if( type_str == "ILS" ) type = flightgear::PROCEDURE_APPROACH_ILS;
192     else if( type_str == "RNAV") type = flightgear::PROCEDURE_APPROACH_RNAV;
193   }
194
195   return ctx.to_nasal
196   (
197     extract( rwy ? rwy->getApproaches(type)
198                  // no runway specified, report them all
199                  : apt.getApproaches(type),
200              &flightgear::Approach::ident )
201   );
202 }
203
204 //------------------------------------------------------------------------------
205 static FGParkingList
206 f_airport_parking(FGAirport& apt, nasal::CallContext ctx)
207 {
208   std::string type = ctx.getArg<std::string>(0);
209   bool only_available = ctx.getArg<bool>(1);
210
211   FGAirportDynamics* dynamics = apt.getDynamics();
212   PositionedIDVec parkings =
213     flightgear::NavDataCache::instance()
214       ->airportItemsOfType(apt.guid(), FGPositioned::PARKING);
215
216   FGParkingList ret;
217   BOOST_FOREACH(PositionedID parking, parkings)
218   {
219     // filter out based on availability and type
220     if( only_available && !dynamics->isParkingAvailable(parking) )
221       continue;
222
223     FGParking* park = dynamics->getParking(parking);
224     if( !type.empty() && (park->getType() != type) )
225       continue;
226
227     ret.push_back(park);
228   }
229
230   return ret;
231 }
232
233 /**
234  * Extract a SGGeod from a nasal function argument list.
235  *
236  * <lat>, <lon>
237  * {"lat": <lat-deg>, "lon": <lon-deg>}
238  * geo.Coord.new() (aka. {"_lat": <lat-rad>, "_lon": <lon-rad>})
239  */
240 static bool extractGeod(nasal::CallContext& ctx, SGGeod& result)
241 {
242   if( !ctx.argc )
243     return false;
244
245   if( ctx.isGhost(0) )
246   {
247     FGPositioned* pos =
248       NasalPositioned::fromNasal(ctx.c, ctx.requireArg<naRef>(0));
249
250     if( pos )
251     {
252       result = pos->geod();
253       ctx.popFront();
254       return true;
255     }
256   }
257   else if( ctx.isHash(0) )
258   {
259     nasal::Hash pos_hash = ctx.requireArg<nasal::Hash>(0);
260
261     // check for manual latitude / longitude names
262     naRef lat = pos_hash.get("lat"),
263           lon = pos_hash.get("lon");
264     if( naIsNum(lat) && naIsNum(lon) )
265     {
266       result = SGGeod::fromDeg( ctx.from_nasal<double>(lon),
267                                 ctx.from_nasal<double>(lat) );
268       ctx.popFront();
269       return true;
270     }
271
272     // geo.Coord uses _lat/_lon in radians
273     // TODO should we check if its really a geo.Coord?
274     lat = pos_hash.get("_lat");
275     lon = pos_hash.get("_lon");
276     if( naIsNum(lat) && naIsNum(lon) )
277     {
278       result = SGGeod::fromRad( ctx.from_nasal<double>(lon),
279                                 ctx.from_nasal<double>(lat) );
280       ctx.popFront();
281       return true;
282     }
283   }
284   else if( ctx.isNumeric(0) && ctx.isNumeric(1) )
285   {
286     // lat, lon
287     result = SGGeod::fromDeg( ctx.requireArg<double>(1),
288                               ctx.requireArg<double>(0) );
289     ctx.popFront(2);
290     return true;
291   }
292
293   return false;
294 }
295
296 /**
297  * Extract position from ctx or return current aircraft position if not given.
298  */
299 static SGGeod getPosition(nasal::CallContext& ctx)
300 {
301   SGGeod pos;
302   if( !extractGeod(ctx, pos) )
303     pos = globals->get_aircraft_position();
304
305   return pos;
306 }
307
308 //------------------------------------------------------------------------------
309 // Returns Nasal ghost for particular or nearest airport of a <type>, or nil
310 // on error.
311 //
312 // airportinfo(<id>);                   e.g. "KSFO"
313 // airportinfo(<type>);                 type := ("airport"|"seaport"|"heliport")
314 // airportinfo()                        same as  airportinfo("airport")
315 // airportinfo(<lat>, <lon> [, <type>]);
316 static naRef f_airportinfo(nasal::CallContext ctx)
317 {
318   SGGeod pos = getPosition(ctx);
319
320   if( ctx.argc > 1 )
321     naRuntimeError(ctx.c, "airportinfo() with invalid function arguments");
322
323   // optional type/ident
324   std::string ident("airport");
325   if( ctx.isString(0) )
326     ident = ctx.requireArg<std::string>(0);
327
328   FGAirport::TypeRunwayFilter filter;
329   if( !filter.fromTypeString(ident) )
330     // user provided an <id>, hopefully
331     return ctx.to_nasal(FGAirport::findByIdent(ident));
332
333   double maxRange = 10000.0; // expose this? or pick a smaller value?
334   return ctx.to_nasal( FGAirport::findClosest(pos, maxRange, &filter) );
335 }
336
337 /**
338  * findAirportsWithinRange([<position>,] <range-nm> [, type])
339  */
340 static naRef f_findAirportsWithinRange(nasal::CallContext ctx)
341 {
342   SGGeod pos = getPosition(ctx);
343   double range_nm = ctx.requireArg<double>(0);
344
345   FGAirport::TypeRunwayFilter filter; // defaults to airports only
346   filter.fromTypeString( ctx.getArg<std::string>(1) );
347
348   FGPositionedList apts = FGPositioned::findWithinRange(pos, range_nm, &filter);
349   FGPositioned::sortByRange(apts, pos);
350
351   return ctx.to_nasal(apts);
352 }
353
354 /**
355  * findAirportsByICAO(<ident/prefix> [, type])
356  */
357 static naRef f_findAirportsByICAO(nasal::CallContext ctx)
358 {
359   std::string prefix = ctx.requireArg<std::string>(0);
360
361   FGAirport::TypeRunwayFilter filter; // defaults to airports only
362   filter.fromTypeString( ctx.getArg<std::string>(1) );
363
364   return ctx.to_nasal( FGPositioned::findAllWithIdent(prefix, &filter, false) );
365 }
366
367 // Returns vector of data hash for navaid of a <type>, nil on error
368 // navaids sorted by ascending distance
369 // navinfo([<lat>,<lon>],[<type>],[<id>])
370 // lat/lon (numeric): use latitude/longitude instead of ac position
371 // type:              ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
372 // id:                (partial) id of the fix
373 // examples:
374 // navinfo("vor")     returns all vors
375 // navinfo("HAM")     return all navaids who's name start with "HAM"
376 // navinfo("vor", "HAM") return all vor who's name start with "HAM"
377 //navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM"
378 //                           sorted by distance relative to lat=34, lon=48
379 static naRef f_navinfo(nasal::CallContext ctx)
380 {
381   SGGeod pos = getPosition(ctx);
382   std::string id = ctx.getArg<std::string>(0);
383
384   FGNavList::TypeFilter filter;
385   if( filter.fromTypeString(id) )
386     id = ctx.getArg<std::string>(1);
387   else if( ctx.argc > 1 )
388     naRuntimeError(ctx.c, "navinfo() already got an ident");
389
390   return ctx.to_nasal( FGNavList::findByIdentAndFreq(pos, id, 0.0, &filter) );
391 }
392
393 //------------------------------------------------------------------------------
394 static naRef f_findWithinRange(nasal::CallContext ctx)
395 {
396   SGGeod pos = getPosition(ctx);
397   double range_nm = ctx.requireArg<double>(0);
398
399     std::string typeSpec = ctx.getArg<std::string>(1);
400     FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
401     
402   FGPositionedList items = FGPositioned::findWithinRange(pos, range_nm, &filter);
403   FGPositioned::sortByRange(items, pos);
404   return ctx.to_nasal(items);
405 }
406
407 static naRef f_findByIdent(nasal::CallContext ctx)
408 {
409   std::string prefix = ctx.requireArg<std::string>(0);
410   std::string typeSpec = ctx.getArg<std::string>(1);
411   FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
412   bool exact = ctx.getArg<bool>(2, false);
413
414   return ctx.to_nasal( FGPositioned::findAllWithIdent(prefix, &filter, exact) );
415 }
416
417 static naRef f_findByName(nasal::CallContext ctx)
418 {
419   std::string prefix = ctx.requireArg<std::string>(0);
420   std::string typeSpec = ctx.getArg<std::string>(1);
421   FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
422   
423   return ctx.to_nasal( FGPositioned::findAllWithName(prefix, &filter, false) );
424 }
425
426 //------------------------------------------------------------------------------
427
428 static naRef f_courseAndDistance(nasal::CallContext ctx)
429 {
430   SGGeod from = globals->get_aircraft_position(), to, pos;
431   bool ok = extractGeod(ctx, pos);
432   if (!ok) {
433     naRuntimeError(ctx.c, "invalid arguments to courseAndDistance");
434   }
435   
436   if (extractGeod(ctx, to)) {
437     from = pos; // we parsed both FROM and TO args, so first was FROM
438   } else {
439     to = pos; // only parsed one arg, so FROM is current
440   }
441   
442   double course, course2, d;
443   SGGeodesy::inverse(from, to, course, course2, d);
444   
445   naRef result = naNewVector(ctx.c);
446   naVec_append(result, naNum(course));
447   naVec_append(result, naNum(d * SG_METER_TO_NM));
448   return result;
449 }
450
451 static naRef f_sortByRange(nasal::CallContext ctx)
452 {
453   FGPositionedList items = ctx.requireArg<FGPositionedList>(0);
454   ctx.popFront();
455   FGPositioned::sortByRange(items, getPosition(ctx));
456   return ctx.to_nasal(items);
457 }
458
459 //------------------------------------------------------------------------------
460 naRef initNasalPositioned_cppbind(naRef globalsRef, naContext c)
461 {
462   NasalPositioned::init("Positioned")
463     .member("id", &FGPositioned::ident)
464     .member("ident", &FGPositioned::ident) // TODO to we really need id and ident?
465     .member("name", &FGPositioned::name)
466     .member("type", &FGPositioned::typeString)
467     .member("lat", &FGPositioned::latitude)
468     .member("lon", &FGPositioned::longitude)
469     .member("elevation", &FGPositioned::elevationM);
470   NasalRunway::init("Runway")
471     .bases<NasalPositioned>();
472   NasalParking::init("Parking")
473     .bases<NasalPositioned>();
474   NasalCommStation::init("CommStation")
475     .bases<NasalPositioned>()
476     .member("frequency", &flightgear::CommStation::freqMHz);
477   NasalNavRecord::init("Navaid")
478     .bases<NasalPositioned>()
479     .member("frequency", &FGNavRecord::get_freq)
480     .member("range_nm", &FGNavRecord::get_range)
481     .member("course", &f_navaid_course);
482
483   NasalFix::init("Fix")
484     .bases<NasalPositioned>();
485   
486   NasalAirport::init("FGAirport")
487     .bases<NasalPositioned>()
488     .member("has_metar", &FGAirport::getMetar)
489     .member("runways", &FGAirport::getRunwayMap)
490     .member("helipads", &FGAirport::getHelipadMap)
491     .member("taxiways", &FGAirport::getTaxiways)
492     .member("pavements", &FGAirport::getPavements)
493     .method("runway", &f_airport_runway)
494     .method("helipad", &f_airport_runway)
495     .method("tower", &FGAirport::getTowerLocation)
496     .method("comms", &f_airport_comms)
497     .method("sids", &f_airport_sids)
498     .method("stars", &f_airport_stars)
499     .method("getApproachList", f_airport_approaches)
500     .method("parking", &f_airport_parking)
501     .method("getSid", &FGAirport::findSIDWithIdent)
502     .method("getStar", &FGAirport::findSTARWithIdent)
503     .method("getIAP", &FGAirport::findApproachWithIdent)
504     .method("tostring", &FGAirport::toString);
505     
506   nasal::Hash globals(globalsRef, c),
507               positioned( globals.createHash("positioned") );
508
509   positioned.set("airportinfo", &f_airportinfo);
510   positioned.set("findAirportsWithinRange", f_findAirportsWithinRange);
511   positioned.set("findAirportsByICAO", &f_findAirportsByICAO);
512   positioned.set("navinfo", &f_navinfo);
513   
514   positioned.set("findWithinRange", &f_findWithinRange);
515   positioned.set("findByIdent", &f_findByIdent);
516   positioned.set("findByName", &f_findByName);
517   positioned.set("courseAndDistance", &f_courseAndDistance);
518   positioned.set("sortByRange", &f_sortByRange);
519   
520   return naNil();
521 }