]> git.mxchange.org Git - flightgear.git/blobdiff - src/Scripting/NasalSys.cxx
Merge branch 'next' into navaids-radio
[flightgear.git] / src / Scripting / NasalSys.cxx
index 72a99bc93219eb5744d820be72d3f9e5131878cf..936c515378a8683961f101ce8e09ad2144fc66c0 100644 (file)
 #include <Main/fg_props.hxx>
 #include <Main/util.hxx>
 #include <Scenery/scenery.hxx>
-#include <Navaids/navrecord.hxx>
+#include <Navaids/navlist.hxx>
 #include <Navaids/procedure.hxx>
+#include <Radio/radio.hxx>
+
 
 #include "NasalSys.hxx"
 
 static FGNasalSys* nasalSys = 0;
 
+// Listener class for loading Nasal modules on demand
+class FGNasalModuleListener : public SGPropertyChangeListener
+{
+public:
+    FGNasalModuleListener(SGPropertyNode* node);
+
+    virtual void valueChanged(SGPropertyNode* node);
+
+private:
+    SGPropertyNode_ptr _node;
+};
+
+FGNasalModuleListener::FGNasalModuleListener(SGPropertyNode* node) : _node(node)
+{
+}
+
+void FGNasalModuleListener::valueChanged(SGPropertyNode*)
+{
+    if (_node->getBoolValue("enabled",false)&&
+        !_node->getBoolValue("loaded",true))
+    {
+        nasalSys->loadPropertyScripts(_node);
+    }
+}
+
 
 // Read and return file contents in a single buffer.  Note use of
 // stat() to get the file size.  This is a win32 function, believe it
@@ -451,10 +478,9 @@ static naRef f_systime(naContext c, naRef me, int argc, naRef* args)
     // Converts from 100ns units in 1601 epoch to unix epoch in sec
     return naNum((t * 1e-7) - 11644473600.0);
 #else
-    time_t t;
     struct timeval td;
-    do { t = time(0); gettimeofday(&td, 0); } while(t != time(0));
-    return naNum(t + 1e-6 * td.tv_usec);
+    gettimeofday(&td, 0);
+    return naNum(td.tv_sec + 1e-6 * td.tv_usec);
 #endif
 }
 
@@ -530,6 +556,27 @@ static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
 #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
 {
@@ -671,6 +718,92 @@ static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
 }
 
 
+// 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 },
@@ -693,6 +826,8 @@ static struct { const char* name; naCFunction func; } funcs[] = {
     { "geodtocart", f_geodtocart },
     { "geodinfo", f_geodinfo },
     { "airportinfo", f_airportinfo },
+    { "navinfo", f_navinfo },
+    { "radioTransmission", f_radioTransmission },
     { 0, 0 }
 };
 
@@ -780,10 +915,19 @@ void FGNasalSys::update(double)
     _context = naNewContext();
 }
 
+bool pathSortPredicate(const SGPath& p1, const SGPath& p2)
+{
+  return p1.file() < p2.file();
+}
+
 // Loads all scripts in given directory 
 void FGNasalSys::loadScriptDirectory(simgear::Dir nasalDir)
 {
     simgear::PathList scripts = nasalDir.children(simgear::Dir::TYPE_FILE, ".nas");
+    // sort scripts, avoid loading sequence effects due to file system's
+    // random directory order
+    std::sort(scripts.begin(), scripts.end(), pathSortPredicate);
+
     for (unsigned int i=0; i<scripts.size(); ++i) {
       SGPath fullpath(scripts[i]);
       SGPath file = fullpath.file();
@@ -817,54 +961,78 @@ void FGNasalSys::loadPropertyScripts()
     SGPropertyNode* nasal = globals->get_props()->getNode("nasal");
     if(!nasal) return;
 
-    for(int i=0; i<nasal->nChildren(); i++) {
+    for(int i=0; i<nasal->nChildren(); i++)
+    {
         SGPropertyNode* n = nasal->getChild(i);
-        bool is_loaded = false;
+        loadPropertyScripts(n);
+    }
+}
 
-        const char* module = n->getName();
-        if(n->hasChild("module"))
-            module = n->getStringValue("module");
-        if (n->getBoolValue("enabled",true))
-        {
-            // allow multiple files to be specified within a single
-            // Nasal module tag
-            int j = 0;
-            SGPropertyNode *fn;
-            bool file_specified = false;
-            while((fn = n->getChild("file", j)) != NULL) {
-                file_specified = true;
-                const char* file = fn->getStringValue();
-                SGPath p(file);
-                if (!p.isAbsolute() || !p.exists())
+// Loads the scripts found under /nasal in the global tree
+void FGNasalSys::loadPropertyScripts(SGPropertyNode* n)
+{
+    bool is_loaded = false;
+
+    const char* module = n->getName();
+    if(n->hasChild("module"))
+        module = n->getStringValue("module");
+    if (n->getBoolValue("enabled",true))
+    {
+        // allow multiple files to be specified within a single
+        // Nasal module tag
+        int j = 0;
+        SGPropertyNode *fn;
+        bool file_specified = false;
+        bool ok=true;
+        while((fn = n->getChild("file", j)) != NULL) {
+            file_specified = true;
+            const char* file = fn->getStringValue();
+            SGPath p(file);
+            if (!p.isAbsolute() || !p.exists())
+            {
+                p = globals->resolve_maybe_aircraft_path(file);
+                if (p.isNull())
                 {
-                    p = globals->resolve_maybe_aircraft_path(file);
+                    SG_LOG(SG_NASAL, SG_ALERT, "Cannot find Nasal script '" <<
+                            file << "' for module '" << module << "'.");
                 }
-                loadModule(p, module);
-                j++;
-            }
-    
-            const char* src = n->getStringValue("script");
-            if(!n->hasChild("script")) src = 0; // Hrm...
-            if(src)
-                createModule(module, n->getPath().c_str(), src, strlen(src));
-    
-            if(!file_specified && !src)
-            {
-                // module no longer exists - clear the archived "enable" flag
-                n->setAttribute(SGPropertyNode::USERARCHIVE,false);
-                SGPropertyNode* node = n->getChild("enabled",0,false);
-                if (node)
-                    node->setAttribute(SGPropertyNode::USERARCHIVE,false);
-
-                SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
-                        "no <file> or <script> defined in " <<
-                        "/nasal/" << module);
             }
-            else
-                is_loaded = true;
+            ok &= p.isNull() ? false : loadModule(p, module);
+            j++;
+        }
+
+        const char* src = n->getStringValue("script");
+        if(!n->hasChild("script")) src = 0; // Hrm...
+        if(src)
+            createModule(module, n->getPath().c_str(), src, strlen(src));
+
+        if(!file_specified && !src)
+        {
+            // module no longer exists - clear the archived "enable" flag
+            n->setAttribute(SGPropertyNode::USERARCHIVE,false);
+            SGPropertyNode* node = n->getChild("enabled",0,false);
+            if (node)
+                node->setAttribute(SGPropertyNode::USERARCHIVE,false);
+
+            SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
+                    "no <file> or <script> defined in " <<
+                    "/nasal/" << module);
         }
-        n->setBoolValue("loaded",is_loaded);
+        else
+            is_loaded = ok;
     }
+    else
+    {
+        SGPropertyNode* enable = n->getChild("enabled");
+        if (enable)
+        {
+            FGNasalModuleListener* listener = new FGNasalModuleListener(n);
+            enable->addChangeListener(listener, false);
+        }
+    }
+    SGPropertyNode* loaded = n->getChild("loaded",0,true);
+    loaded->setAttribute(SGPropertyNode::PRESERVE,true);
+    loaded->setBoolValue(is_loaded);
 }
 
 // Logs a runtime error, with stack trace, to the FlightGear log stream
@@ -884,7 +1052,7 @@ void FGNasalSys::logError(naContext context)
 // Reads a script file, executes it, and places the resulting
 // namespace into the global namespace under the specified module
 // name.
-void FGNasalSys::loadModule(SGPath file, const char* module)
+bool FGNasalSys::loadModule(SGPath file, const char* module)
 {
     int len = 0;
     char* buf = readfile(file.c_str(), &len);
@@ -892,25 +1060,26 @@ void FGNasalSys::loadModule(SGPath file, const char* module)
         SG_LOG(SG_NASAL, SG_ALERT,
                "Nasal error: could not read script file " << file.c_str()
                << " into module " << module);
-        return;
+        return false;
     }
 
-    createModule(module, file.c_str(), buf, len);
+    bool ok = createModule(module, file.c_str(), buf, len);
     delete[] buf;
+    return ok;
 }
 
 // Parse and run.  Save the local variables namespace, as it will
 // become a sub-object of globals.  The optional "arg" argument can be
 // used to pass an associated property node to the module, which can then
 // be accessed via cmdarg().  (This is, for example, used by XML dialogs.)
-void FGNasalSys::createModule(const char* moduleName, const char* fileName,
+bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
                               const char* src, int len,
                               const SGPropertyNode* cmdarg,
                               int argc, naRef* args)
 {
     naRef code = parse(fileName, src, len);
     if(naIsNil(code))
-        return;
+        return false;
 
     // See if we already have a module hash to use.  This allows the
     // user to, for example, add functions to the built-in math
@@ -925,6 +1094,7 @@ void FGNasalSys::createModule(const char* moduleName, const char* fileName,
 
     call(code, argc, args, locals);
     hashset(_globals, moduleName, locals);
+    return true;
 }
 
 void FGNasalSys::deleteModule(const char* moduleName)
@@ -1080,7 +1250,7 @@ naRef FGNasalSys::setListener(naContext c, int argc, naRef* args)
     FGNasalListener *nl = new FGNasalListener(node, code, this,
             gcSave(code), _listenerId, init, type);
 
-    node->addChangeListener(nl, init);
+    node->addChangeListener(nl, init != 0);
 
     _listener[_listenerId] = nl;
     return naNum(_listenerId++);