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