]> git.mxchange.org Git - flightgear.git/blobdiff - src/Scripting/NasalSys.cxx
Start refactoring how FGPositioned classes are exposed to Nasal, more to come.
[flightgear.git] / src / Scripting / NasalSys.cxx
index 936c515378a8683961f101ce8e09ad2144fc66c0..5f67b2c13c63fa63eb3920c85a7134ee144ca66f 100644 (file)
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/misc/sg_dir.hxx>
 #include <simgear/misc/interpolator.hxx>
-#include <simgear/scene/material/mat.hxx>
 #include <simgear/structure/commands.hxx>
 #include <simgear/math/sg_geodesy.hxx>
 #include <simgear/structure/event_mgr.hxx>
 
-#include <Airports/runways.hxx>
-#include <Airports/simple.hxx>
+#include "NasalSys.hxx"
+#include "NasalPositioned.hxx"
 #include <Main/globals.hxx>
-#include <Main/fg_props.hxx>
 #include <Main/util.hxx>
-#include <Scenery/scenery.hxx>
-#include <Navaids/navlist.hxx>
-#include <Navaids/procedure.hxx>
-#include <Radio/radio.hxx>
+#include <Main/fg_props.hxx>
 
-
-#include "NasalSys.hxx"
+using std::map;
 
 static FGNasalSys* nasalSys = 0;
 
@@ -216,7 +210,7 @@ static naRef f_getprop(naContext c, naRef me, int argc, naRef* args)
         {
         double dv = p->getDoubleValue();
         if (osg::isNaN(dv)) {
-          SG_LOG(SG_GENERAL, SG_ALERT, "Nasal getprop: property " << p->getPath() << " is NaN");
+          SG_LOG(SG_NASAL, SG_ALERT, "Nasal getprop: property " << p->getPath() << " is NaN");
           return naNil();
         }
         
@@ -295,7 +289,7 @@ static naRef f_print(naContext c, naRef me, int argc, naRef* args)
         if(naIsNil(s)) continue;
         buf += naStr_data(s);
     }
-    SG_LOG(SG_GENERAL, SG_ALERT, buf);
+    SG_LOG(SG_NASAL, SG_ALERT, buf);
     return naNum(buf.length());
 }
 
@@ -484,326 +478,6 @@ static naRef f_systime(naContext c, naRef me, int argc, naRef* args)
 #endif
 }
 
-// Convert a cartesian point to a geodetic lat/lon/altitude.
-static naRef f_carttogeod(naContext c, naRef me, int argc, naRef* args)
-{
-    double lat, lon, alt, xyz[3];
-    if(argc != 3) naRuntimeError(c, "carttogeod() expects 3 arguments");
-    for(int i=0; i<3; i++)
-        xyz[i] = naNumValue(args[i]).num;
-    sgCartToGeod(xyz, &lat, &lon, &alt);
-    lat *= SG_RADIANS_TO_DEGREES;
-    lon *= SG_RADIANS_TO_DEGREES;
-    naRef vec = naNewVector(c);
-    naVec_append(vec, naNum(lat));
-    naVec_append(vec, naNum(lon));
-    naVec_append(vec, naNum(alt));
-    return vec;
-}
-
-// Convert a geodetic lat/lon/altitude to a cartesian point.
-static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args)
-{
-    if(argc != 3) naRuntimeError(c, "geodtocart() expects 3 arguments");
-    double lat = naNumValue(args[0]).num * SG_DEGREES_TO_RADIANS;
-    double lon = naNumValue(args[1]).num * SG_DEGREES_TO_RADIANS;
-    double alt = naNumValue(args[2]).num;
-    double xyz[3];
-    sgGeodToCart(lat, lon, alt, xyz);
-    naRef vec = naNewVector(c);
-    naVec_append(vec, naNum(xyz[0]));
-    naVec_append(vec, naNum(xyz[1]));
-    naVec_append(vec, naNum(xyz[2]));
-    return vec;
-}
-
-// For given geodetic point return array with elevation, and a material data
-// hash, or nil if there's no information available (tile not loaded). If
-// information about the material isn't available, then nil is returned instead
-// of the hash.
-static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
-{
-#define HASHSET(s,l,n) naHash_set(matdata, naStr_fromdata(naNewString(c),s,l),n)
-    if(argc < 2 || argc > 3)
-        naRuntimeError(c, "geodinfo() expects 2 or 3 arguments: lat, lon [, maxalt]");
-    double lat = naNumValue(args[0]).num;
-    double lon = naNumValue(args[1]).num;
-    double elev = argc == 3 ? naNumValue(args[2]).num : 10000;
-    const SGMaterial *mat;
-    SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
-    if(!globals->get_scenery()->get_elevation_m(geod, elev, &mat))
-        return naNil();
-    naRef vec = naNewVector(c);
-    naVec_append(vec, naNum(elev));
-    naRef matdata = naNil();
-    if(mat) {
-        matdata = naNewHash(c);
-        naRef names = naNewVector(c);
-        const vector<string> n = mat->get_names();
-        for(unsigned int i=0; i<n.size(); i++)
-            naVec_append(names, naStr_fromdata(naNewString(c),
-                    const_cast<char*>(n[i].c_str()), n[i].size()));
-        HASHSET("names", 5, names);
-        HASHSET("solid", 5, naNum(mat->get_solid()));
-        HASHSET("friction_factor", 15, naNum(mat->get_friction_factor()));
-        HASHSET("rolling_friction", 16, naNum(mat->get_rolling_friction()));
-        HASHSET("load_resistance", 15, naNum(mat->get_load_resistance()));
-        HASHSET("bumpiness", 9, naNum(mat->get_bumpiness()));
-        HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
-    }
-    naVec_append(vec, matdata);
-    return vec;
-#undef HASHSET
-}
-
-// Expose a radio transmission interface to Nasal.
-static naRef f_radioTransmission(naContext c, naRef me, int argc, naRef* args)
-{
-    double lat, lon, elev, heading, pitch;
-    if(argc != 5) naRuntimeError(c, "radioTransmission() expects 5 arguments");
-    for(int i=0; i<argc; i++) {
-        if(naIsNil(args[i]))
-               return naNil();
-    }
-    lat = naNumValue(args[0]).num;
-    lon = naNumValue(args[1]).num;
-    elev = naNumValue(args[2]).num;
-    heading = naNumValue(args[3]).num;
-    pitch = naNumValue(args[4]).num;
-    SGGeod geod = SGGeod::fromDegM(lon, lat, elev * SG_FEET_TO_METER);
-    FGRadioTransmission *radio = new FGRadioTransmission;
-    double signal = radio->receiveBeacon(geod, heading, pitch);
-    delete radio;
-    return naNum(signal);
-}
-
-
-class AirportInfoFilter : public FGAirport::AirportFilter
-{
-public:
-    AirportInfoFilter() : type(FGPositioned::AIRPORT) {
-    }
-
-    virtual FGPositioned::Type minType() const {
-        return type;
-    }
-
-    virtual FGPositioned::Type maxType() const {
-        return type;
-    }
-
-    FGPositioned::Type type;
-};
-
-// Returns data hash for particular or nearest airport of a <type>, or nil
-// on error.
-//
-// airportinfo(<id>);                   e.g. "KSFO"
-// airportinfo(<type>);                 type := ("airport"|"seaport"|"heliport")
-// airportinfo()                        same as  airportinfo("airport")
-// airportinfo(<lat>, <lon> [, <type>]);
-static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
-{
-    static SGConstPropertyNode_ptr latn = fgGetNode("/position/latitude-deg", true);
-    static SGConstPropertyNode_ptr lonn = fgGetNode("/position/longitude-deg", true);
-    SGGeod pos;
-    FGAirport* apt = NULL;
-
-    if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
-        pos = SGGeod::fromDeg(args[1].num, args[0].num);
-        args += 2;
-        argc -= 2;
-    } else {
-        pos = SGGeod::fromDeg(lonn->getDoubleValue(), latn->getDoubleValue());
-    }
-
-    double maxRange = 10000.0; // expose this? or pick a smaller value?
-
-    AirportInfoFilter filter; // defaults to airports only
-
-    if(argc == 0) {
-        // fall through and use AIRPORT
-    } else if(argc == 1 && naIsString(args[0])) {
-        const char *s = naStr_data(args[0]);
-        if(!strcmp(s, "airport")) filter.type = FGPositioned::AIRPORT;
-        else if(!strcmp(s, "seaport")) filter.type = FGPositioned::SEAPORT;
-        else if(!strcmp(s, "heliport")) filter.type = FGPositioned::HELIPORT;
-        else {
-            // user provided an <id>, hopefully
-            apt = FGAirport::findByIdent(s);
-            if (!apt) {
-                // return nil here, but don't raise a runtime error; this is a
-                // legitamate way to validate an ICAO code, for example in a
-                // dialog box or similar.
-                return naNil();
-            }
-        }
-    } else {
-        naRuntimeError(c, "airportinfo() with invalid function arguments");
-        return naNil();
-    }
-
-    if(!apt) {
-        apt = FGAirport::findClosest(pos, maxRange, &filter);
-        if(!apt) return naNil();
-    }
-
-    string id = apt->ident();
-    string name = apt->name();
-
-    // set runway hash
-    naRef rwys = naNewHash(c);
-    for(unsigned int r=0; r<apt->numRunways(); ++r) {
-        FGRunway* rwy(apt->getRunwayByIndex(r));
-
-        naRef rwyid = naStr_fromdata(naNewString(c),
-                      const_cast<char *>(rwy->ident().c_str()),
-                      rwy->ident().length());
-
-        naRef rwydata = naNewHash(c);
-#define HASHSET(s,l,n) naHash_set(rwydata, naStr_fromdata(naNewString(c),s,l),n)
-        HASHSET("id", 2, rwyid);
-        HASHSET("lat", 3, naNum(rwy->latitude()));
-        HASHSET("lon", 3, naNum(rwy->longitude()));
-        HASHSET("heading", 7, naNum(rwy->headingDeg()));
-        HASHSET("length", 6, naNum(rwy->lengthM()));
-        HASHSET("width", 5, naNum(rwy->widthM()));
-        HASHSET("threshold", 9, naNum(rwy->displacedThresholdM()));
-        HASHSET("stopway", 7, naNum(rwy->stopwayM()));
-        
-        if (rwy->ILS()) {
-          HASHSET("ils_frequency_mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0));
-        }
-        
-        std::vector<flightgear::SID*> sids(rwy->getSIDs());
-        naRef sidVec = naNewVector(c);
-        
-        for (unsigned int s=0; s < sids.size(); ++s) {
-          naRef procId = naStr_fromdata(naNewString(c),
-                    const_cast<char *>(sids[s]->ident().c_str()),
-                    sids[s]->ident().length());
-          naVec_append(sidVec, procId);
-        }
-        HASHSET("sids", 4, sidVec); 
-        
-        std::vector<flightgear::STAR*> stars(rwy->getSTARs());
-        naRef starVec = naNewVector(c);
-      
-        for (unsigned int s=0; s < stars.size(); ++s) {
-          naRef procId = naStr_fromdata(naNewString(c),
-                    const_cast<char *>(stars[s]->ident().c_str()),
-                    stars[s]->ident().length());
-          naVec_append(starVec, procId);
-        }
-        HASHSET("stars", 5, starVec); 
-
-#undef HASHSET
-        naHash_set(rwys, rwyid, rwydata);
-    }
-
-    // set airport hash
-    naRef aptdata = naNewHash(c);
-#define HASHSET(s,l,n) naHash_set(aptdata, naStr_fromdata(naNewString(c),s,l),n)
-    HASHSET("id", 2, naStr_fromdata(naNewString(c),
-            const_cast<char *>(id.c_str()), id.length()));
-    HASHSET("name", 4, naStr_fromdata(naNewString(c),
-            const_cast<char *>(name.c_str()), name.length()));
-    HASHSET("lat", 3, naNum(apt->getLatitude()));
-    HASHSET("lon", 3, naNum(apt->getLongitude()));
-    HASHSET("elevation", 9, naNum(apt->getElevation() * SG_FEET_TO_METER));
-    HASHSET("has_metar", 9, naNum(apt->getMetar()));
-    HASHSET("runways", 7, rwys);
-#undef HASHSET
-    return aptdata;
-}
-
-
-// Returns vector of data hash for navaid of a <type>, nil on error
-// navaids sorted by ascending distance 
-// navinfo([<lat>,<lon>],[<type>],[<id>])
-// lat/lon (numeric): use latitude/longitude instead of ac position
-// type:              ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
-// id:                (partial) id of the fix
-// examples:
-// navinfo("vor")     returns all vors
-// navinfo("HAM")     return all navaids who's name start with "HAM"
-// navinfo("vor", "HAM") return all vor who's name start with "HAM"
-//navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM" 
-//                           sorted by distance relative to lat=34, lon=48
-static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
-{
-    static SGConstPropertyNode_ptr latn = fgGetNode("/position/latitude-deg", true);
-    static SGConstPropertyNode_ptr lonn = fgGetNode("/position/longitude-deg", true);
-    SGGeod pos;
-
-    if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
-        pos = SGGeod::fromDeg(args[1].num, args[0].num);
-        args += 2;
-        argc -= 2;
-    } else {
-        pos = SGGeod::fromDeg(lonn->getDoubleValue(), latn->getDoubleValue());
-    }
-
-    FGPositioned::Type type = FGPositioned::INVALID;
-    nav_list_type navlist;
-    const char * id = "";
-
-    if(argc > 0 && naIsString(args[0])) {
-        const char *s = naStr_data(args[0]);
-        if(!strcmp(s, "any")) type = FGPositioned::INVALID;
-        else if(!strcmp(s, "fix")) type = FGPositioned::FIX;
-        else if(!strcmp(s, "vor")) type = FGPositioned::VOR;
-        else if(!strcmp(s, "ndb")) type = FGPositioned::NDB;
-        else if(!strcmp(s, "ils")) type = FGPositioned::ILS;
-        else if(!strcmp(s, "dme")) type = FGPositioned::DME;
-        else if(!strcmp(s, "tacan")) type = FGPositioned::TACAN;
-        else id = s; // this is an id
-        ++args;
-        --argc;
-    } 
-
-    if(argc > 0 && naIsString(args[0])) {
-        if( *id != 0 ) {
-            naRuntimeError(c, "navinfo() called with navaid id");
-            return naNil();
-        }
-        id = naStr_data(args[0]);
-        ++args;
-        --argc;
-    }
-
-    if( argc > 0 ) {
-        naRuntimeError(c, "navinfo() called with too many arguments");
-        return naNil();
-    }
-
-    navlist = globals->get_navlist()->findByIdentAndFreq( pos, id, 0.0, type );
-
-    naRef reply = naNewVector(c);
-    for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
-        const FGNavRecord * nav = *it;
-
-        // set navdata hash
-        naRef navdata = naNewHash(c);
-#define HASHSET(s,l,n) naHash_set(navdata, naStr_fromdata(naNewString(c),s,l),n)
-        HASHSET("id", 2, naStr_fromdata(naNewString(c),
-            const_cast<char *>(nav->ident().c_str()), nav->ident().length()));
-        HASHSET("name", 4, naStr_fromdata(naNewString(c),
-            const_cast<char *>(nav->name().c_str()), nav->name().length()));
-        HASHSET("frequency", 9, naNum(nav->get_freq()));
-        HASHSET("lat", 3, naNum(nav->get_lat()));
-        HASHSET("lon", 3, naNum(nav->get_lon()));
-        HASHSET("elevation", 9, naNum(nav->get_elev_ft() * SG_FEET_TO_METER));
-        HASHSET("type", 4, naStr_fromdata(naNewString(c),
-            const_cast<char *>(nav->nameForType(nav->type())), strlen(nav->nameForType(nav->type()))));
-        HASHSET("distance", 8, naNum(SGGeodesy::distanceNm( pos, nav->geod() ) * SG_NM_TO_METER ) );
-        HASHSET("bearing", 7, naNum(SGGeodesy::courseDeg( pos, nav->geod() ) ) );
-#undef HASHSET
-        naVec_append( reply, navdata );
-    }
-    return reply;
-}
-
 // Table of extension functions.  Terminate with zeros.
 static struct { const char* name; naCFunction func; } funcs[] = {
     { "getprop",   f_getprop },
@@ -822,12 +496,6 @@ static struct { const char* name; naCFunction func; } funcs[] = {
     { "resolvepath", f_resolveDataPath },
     { "parsexml", f_parsexml },
     { "systime", f_systime },
-    { "carttogeod", f_carttogeod },
-    { "geodtocart", f_geodtocart },
-    { "geodinfo", f_geodinfo },
-    { "airportinfo", f_airportinfo },
-    { "navinfo", f_navinfo },
-    { "radioTransmission", f_radioTransmission },
     { 0, 0 }
 };
 
@@ -861,6 +529,8 @@ void FGNasalSys::init()
         hashset(_globals, funcs[i].name,
                 naNewFunc(_context, naNewCCode(_context, funcs[i].func)));
 
+    initNasalPositioned(_globals, _context);
+  
     // And our SGPropertyNode wrapper
     hashset(_globals, "props", genPropsModule());
 
@@ -901,6 +571,19 @@ void FGNasalSys::update(double)
         _dead_listener.clear();
     }
 
+    if (!_loadList.empty())
+    {
+        // process Nasal load hook (only one per update loop to avoid excessive lags)
+        _loadList.pop()->load();
+    }
+    else
+    if (!_unloadList.empty())
+    {
+        // process pending Nasal unload hooks after _all_ load hooks were processed
+        // (only unload one per update loop to avoid excessive lags)
+        _unloadList.pop()->unload();
+    }
+
     // The global context is a legacy thing.  We use dynamically
     // created contexts for naCall() now, so that we can call them
     // recursively.  But there are still spots that want to use it for
@@ -1381,36 +1064,26 @@ bool FGNasalListener::changed(SGPropertyNode* node)
 
 unsigned int FGNasalModelData::_module_id = 0;
 
-void FGNasalModelData::modelLoaded(const string& path, SGPropertyNode *prop,
-                                   osg::Node *)
+void FGNasalModelData::load()
 {
-    if(!prop)
-        return;
-    SGPropertyNode *nasal = prop->getNode("nasal");
-    if(!nasal)
-        return;
-
-    SGPropertyNode *load = nasal->getNode("load");
-    _unload = nasal->getNode("unload");
-    if(!load && !_unload)
-        return;
-
     std::stringstream m;
     m << "__model" << _module_id++;
     _module = m.str();
 
-    const char *s = load ? load->getStringValue() : "";
+    SG_LOG(SG_NASAL, SG_DEBUG, "Loading nasal module " << _module.c_str());
+
+    const char *s = _load ? _load->getStringValue() : "";
 
     naRef arg[2];
     arg[0] = nasalSys->propNodeGhost(_root);
-    arg[1] = nasalSys->propNodeGhost(prop);
-    nasalSys->createModule(_module.c_str(), path.c_str(), s, strlen(s),
+    arg[1] = nasalSys->propNodeGhost(_prop);
+    nasalSys->createModule(_module.c_str(), _path.c_str(), s, strlen(s),
                            _root, 2, arg);
 }
 
-FGNasalModelData::~FGNasalModelData()
+void FGNasalModelData::unload()
 {
-    if(_module.empty())
+    if (_module.empty())
         return;
 
     if(!nasalSys) {
@@ -1419,14 +1092,52 @@ FGNasalModelData::~FGNasalModelData()
         return;
     }
 
-    if(_unload) {
+    SG_LOG(SG_NASAL, SG_DEBUG, "Unloading nasal module " << _module.c_str());
+
+    if (_unload)
+    {
         const char *s = _unload->getStringValue();
         nasalSys->createModule(_module.c_str(), _module.c_str(), s, strlen(s), _root);
     }
+
     nasalSys->deleteModule(_module.c_str());
 }
 
+void FGNasalModelDataProxy::modelLoaded(const string& path, SGPropertyNode *prop,
+                                   osg::Node *)
+{
+    if(!nasalSys) {
+        SG_LOG(SG_NASAL, SG_WARN, "Trying to run a <load> script "
+                "without Nasal subsystem present.");
+        return;
+    }
+
+    if(!prop)
+        return;
+
+    SGPropertyNode *nasal = prop->getNode("nasal");
+    if(!nasal)
+        return;
+
+    SGPropertyNode* load   = nasal->getNode("load");
+    SGPropertyNode* unload = nasal->getNode("unload");
+
+    if ((!load) && (!unload))
+        return;
+
+    _data = new FGNasalModelData(_root, path, prop, load, unload);
+
+    // register Nasal module to be created and loaded in the main thread.
+    nasalSys->registerToLoad(_data);
+}
 
+FGNasalModelDataProxy::~FGNasalModelDataProxy()
+{
+    // when necessary, register Nasal module to be destroyed/unloaded
+    // in the main thread.
+    if ((_data.valid())&&(nasalSys))
+        nasalSys->registerToUnload(_data);
+}
 
 // NasalXMLVisitor class: handles EasyXML visitor callback for parsexml()
 //