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