FGRadar: Difference between revisions

32,791 bytes added ,  16 November 2016
m (→‎Known Issues: regressions)
 
(27 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{Affected by Canvas|Canvas Radar}}
{{Infobox Software
{{Infobox Software
| title                  = FGRadar
| title                  = FGRadar
Line 8: Line 10:
| writtenin              = C++
| writtenin              = C++
| os                    = Cross platform
| os                    = Cross platform
| developmentstatus      = Active (as of 10/2012)
| developmentstatus      = Put on hold (as of 01/2013)
| developmentprogress    = {{Progressbar|20}}  
| developmentprogress    = {{Progressbar|20}}  
| license                = [[GNU General Public License]] v3
| license                = [[GNU General Public License]] v3
| website                = https://gitorious.org/fgradar
| website                = {{gitorious source|proj=fgradar|repo=fgradar|full=1}} [https://github.com/hamzaalloush/fgradar-clone]
}}
}}
== Building ==
{{See also|FGTraffic}}
Regardless of the status of '''fgradar''', the underlying framework related code may still be useful for related projects, so that it may make sense to maintain a working copy of this code <ref>{{cite web
  |url    =  https://forum.flightgear.org/viewtopic.php?p=298661#p298661
  |title  =  <nowiki> Re: FGTraffic 2020: Road map for a new AI traffic system des </nowiki>
  |author =  <nowiki> Hooray </nowiki>
  |date  =  Nov 13th, 2016
  |added  =  Nov 13th, 2016
  |script_version = 0.40
  }}</ref>.
As of 11/2016, the original code no longer builds against SimGear 2016.4.0, i.e. the following changes are required for the code to build again (which also updates the property module):
<syntaxhighlight lang="diff">diff --git a/CMakeLists.txt b/CMakeLists.txt
index 92342a1..eac8ff3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,7 +30,7 @@ string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
include(cpack_config)
# Check for SimGear (REQUIRED).
-find_package(SimGear 2.9.0 REQUIRED)
+find_package(SimGear 2016.4.0 REQUIRED)
if (SIMGEAR_FOUND)
  include_directories(${SIMGEAR_INCLUDE_DIR})
  set(LIBS ${LIBS} ${SIMGEAR_CORE_LIBRARIES}
diff --git a/src/scripting/nasal-props.cxx b/src/scripting/nasal-props.cxx
index d38401d..4075c22 100644
--- a/src/scripting/nasal-props.cxx
+++ b/src/scripting/nasal-props.cxx
@@ -1,4 +1,3 @@
-
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif
@@ -25,17 +24,17 @@ using namespace std;
static void propNodeGhostDestroy(void* ghost)
{
-    SGPropertyNode_ptr* prop = (SGPropertyNode_ptr*)ghost;
-    delete prop;
+  SGPropertyNode* prop = static_cast<SGPropertyNode*>(ghost);
+  if (!SGPropertyNode::put(prop)) delete prop;
}
naGhostType PropNodeGhostType = { propNodeGhostDestroy, "prop" };
-static naRef propNodeGhostCreate(naContext c, SGPropertyNode* n)
+naRef propNodeGhostCreate(naContext c, SGPropertyNode* ghost)
{
-    if(!n) return naNil();
-    SGPropertyNode_ptr* ghost = new SGPropertyNode_ptr(n);
-    return naNewGhost(c, &PropNodeGhostType, ghost);
+  if(!ghost) return naNil();
+  SGPropertyNode::get(ghost);
+  return naNewGhost(c, &PropNodeGhostType, ghost);
}
naRef FGNasalSys::propNodeGhost(SGPropertyNode* handle)
@@ -47,36 +46,67 @@ SGPropertyNode* ghostToPropNode(naRef ref)
{
  if (!naIsGhost(ref) || (naGhost_type(ref) != &PropNodeGhostType))
    return NULL;
-  SGPropertyNode_ptr* pp = (SGPropertyNode_ptr*) naGhost_ptr(ref);
-  return pp->ptr();
+
+  return static_cast<SGPropertyNode*>(naGhost_ptr(ref));
}
#define NASTR(s) s ? naStr_fromdata(naNewString(c),(char*)(s),strlen(s)) : naNil()
//
// Standard header for the extension functions.  It turns the "ghost"
-// found in arg[0] into a SGPropertyNode_ptr*, and then "unwraps" the
+// found in arg[0] into a SGPropertyNode_ptr, and then "unwraps" the
// vector found in the second argument into a normal-looking args
// array.  This allows the Nasal handlers to do things like:
//  Node.getChild = func { _getChild(me.ghost, arg) }
//
-#define NODENOARG()                                                      \
-    if(argc < 2 || !naIsGhost(args[0]) ||                              \
-      naGhost_type(args[0]) != &PropNodeGhostType)                      \
-        naRuntimeError(c, "bad argument to props function");            \
-    SGPropertyNode_ptr* node = (SGPropertyNode_ptr*)naGhost_ptr(args[0]);
-
-#define NODEARG()                                                      \
-    NODENOARG(); \
+#define NODENOARG()                                                            \
+    if(argc < 2 || !naIsGhost(args[0]) ||                                      \
+        naGhost_type(args[0]) != &PropNodeGhostType)                          \
+        naRuntimeError(c, "bad argument to props function");                  \
+    SGPropertyNode_ptr node = static_cast<SGPropertyNode*>(naGhost_ptr(args[0]));
+
+#define NODEARG()                                                              \
+    NODENOARG();                                                              \
    naRef argv = args[1]
+//
+// Pops the first argument as a relative path if the first condition
+// is true (e.g. argc > 1 for getAttribute) and if it is a string.
+// If the second confition is true, then another is popped to specify
+// if the node should be created (i.e. like the second argument to
+// getNode())
+//
+// Note that this makes the function return nil if the node doesn't
+// exist, so all functions with a relative_path parameter will
+// return nil if the specified node does not exist.
+//
+#define MOVETARGET(cond1, create)                                              \
+    if(cond1) {                                                                \
+        naRef name = naVec_get(argv, 0);                                      \
+        if(naIsString(name)) {                                                \
+            try {                                                              \
+                node = node->getNode(naStr_data(name), create);                \
+            } catch(const string& err) {                                      \
+                naRuntimeError(c, (char *)err.c_str());                        \
+                return naNil();                                                \
+            }                                                                  \
+            if(!node) return naNil();                                          \
+            naVec_removefirst(argv); /* pop only if we were successful */      \
+        }                                                                      \
+    }
+
+
+// Get the type of a property (returns a string).
+// Forms:
+//    props.Node.getType(string relative_path);
+//    props.Node.getType();
static naRef f_getType(naContext c, naRef me, int argc, naRef* args)
{
    using namespace simgear;
-    NODENOARG();
+    NODEARG();
+    MOVETARGET(naVec_size(argv) > 0, false);
    const char* t = "unknown";
-    switch((*node)->getType()) {
+    switch(node->getType()) {
    case props::NONE:  t = "NONE";  break;
    case props::ALIAS:  t = "ALIAS";  break;
    case props::BOOL:  t = "BOOL";  break;
@@ -93,20 +123,28 @@ static naRef f_getType(naContext c, naRef me, int argc, naRef* args)
    return NASTR(t);
}
+
+// Get an attribute of a property by name (returns true/false).
+// Forms:
+//    props.Node.getType(string relative_path,
+//                      string attribute_name);
+//    props.Node.getType(string attribute_name);
static naRef f_getAttribute(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
-    if(naVec_size(argv) == 0) return naNum(unsigned((*node)->getAttributes()));
+    if(naVec_size(argv) == 0) return naNum(unsigned(node->getAttributes()));
+    MOVETARGET(naVec_size(argv) > 1, false);
    naRef val = naVec_get(argv, 0);
    const char *a = naStr_data(val);
    SGPropertyNode::Attribute attr;
    if(!a) a = "";
    if(!strcmp(a, "last")) return naNum(SGPropertyNode::LAST_USED_ATTRIBUTE);
-    else if(!strcmp(a, "children"))    return naNum((*node)->nChildren());
-    else if(!strcmp(a, "listeners"))  return naNum((*node)->nListeners());
-    else if(!strcmp(a, "references"))  return naNum(node->getNumRefs());
-    else if(!strcmp(a, "tied"))        return naNum((*node)->isTied());
-    else if(!strcmp(a, "alias"))      return naNum((*node)->isAlias());
+    else if(!strcmp(a, "children"))    return naNum(node->nChildren());
+    else if(!strcmp(a, "listeners"))  return naNum(node->nListeners());
+    // Number of references without instance used in this function
+    else if(!strcmp(a, "references"))  return naNum(node.getNumRefs() - 1);
+    else if(!strcmp(a, "tied"))        return naNum(node->isTied());
+    else if(!strcmp(a, "alias"))      return naNum(node->isAlias());
    else if(!strcmp(a, "readable"))    attr = SGPropertyNode::READ;
    else if(!strcmp(a, "writable"))    attr = SGPropertyNode::WRITE;
    else if(!strcmp(a, "archive"))    attr = SGPropertyNode::ARCHIVE;
@@ -118,16 +156,26 @@ static naRef f_getAttribute(naContext c, naRef me, int argc, naRef* args)
        naRuntimeError(c, "props.getAttribute() with invalid attribute");
        return naNil();
    }
-    return naNum((*node)->getAttribute(attr));
+    return naNum(node->getAttribute(attr));
}
+
+// Set an attribute by name and boolean value or raw (bitmasked) number.
+// Forms:
+//    props.Node.setAttribute(string relative_path,
+//                            string attribute_name,
+//                            bool value);
+//    props.Node.setAttribute(string attribute_name,
+//                            bool value);
+//    props.Node.setArtribute(int attributes);
static naRef f_setAttribute(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
+    MOVETARGET(naVec_size(argv) > 2, false);
    naRef val = naVec_get(argv, 0);
    if(naVec_size(argv) == 1 && naIsNum(val)) {
-        naRef ret = naNum((*node)->getAttributes());
-        (*node)->setAttributes((int)val.num);
+        naRef ret = naNum(node->getAttributes());
+        node->setAttributes((int)val.num);
        return ret;
    }
    SGPropertyNode::Attribute attr;
@@ -144,21 +192,44 @@ static naRef f_setAttribute(naContext c, naRef me, int argc, naRef* args)
        naRuntimeError(c, "props.setAttribute() with invalid attribute");
        return naNil();
    }
-    naRef ret = naNum((*node)->getAttribute(attr));
-    (*node)->setAttribute(attr, naTrue(naVec_get(argv, 1)) ? true : false);
+    naRef ret = naNum(node->getAttribute(attr));
+    node->setAttribute(attr, naTrue(naVec_get(argv, 1)) ? true : false);
    return ret;
}
+
+// Get the simple name of this node.
+// Forms:
+//    props.Node.getName();
static naRef f_getName(naContext c, naRef me, int argc, naRef* args)
{
    NODENOARG();
-    return NASTR((*node)->getName());
+    return NASTR(node->getName());
}
+
+// Get the index of this node.
+// Forms:
+//    props.Node.getIndex();
static naRef f_getIndex(naContext c, naRef me, int argc, naRef* args)
{
    NODENOARG();
-    return naNum((*node)->getIndex());
+    return naNum(node->getIndex());
+}
+
+// Check if other_node refers to the same as this node.
+// Forms:
+//    props.Node.equals(other_node);
+static naRef f_equals(naContext c, naRef me, int argc, naRef* args)
+{
+    NODEARG();
+
+    naRef rhs = naVec_get(argv, 0);
+    if( !naIsGhost(rhs) || naGhost_type(rhs) != &PropNodeGhostType )
+      return naNum(false);
+
+    SGPropertyNode* node_rhs = static_cast<SGPropertyNode*>(naGhost_ptr(rhs));
+    return naNum(node.ptr() == node_rhs);
}
template<typename T>
@@ -173,18 +244,24 @@ naRef makeVectorFromVec(naContext c, const T& vec)
    return vector;
}
+
+// Get the value of a node, with or without a relative path.
+// Forms:
+//    props.Node.getValue(string relative_path);
+//    props.Node.getValue();
static naRef f_getValue(naContext c, naRef me, int argc, naRef* args)
{
    using namespace simgear;
-    NODENOARG();
-    switch((*node)->getType()) {
+    NODEARG();
+    MOVETARGET(naVec_size(argv) > 0, false);
+    switch(node->getType()) {
    case props::BOOL:  case props::INT:
    case props::LONG:  case props::FLOAT:
    case props::DOUBLE:
    {
-        double dv = (*node)->getDoubleValue();
-        if (osg::isNaN(dv)) {
-          SG_LOG(SG_NASAL, SG_ALERT, "Nasal getValue: property " << (*node)->getPath() << " is NaN");
+        double dv = node->getDoubleValue();
+        if (SGMisc<double>::isNaN(dv)) {
+          SG_LOG(SG_NASAL, SG_ALERT, "Nasal getValue: property " << node->getPath() << " is NaN");
          return naNil();
        }
@@ -193,11 +270,11 @@ static naRef f_getValue(naContext c, naRef me, int argc, naRef* args)
    case props::STRING:
    case props::UNSPECIFIED:
-        return NASTR((*node)->getStringValue());
+        return NASTR(node->getStringValue());
    case props::VEC3D:
-        return makeVectorFromVec(c, (*node)->getValue<SGVec3d>());
+        return makeVectorFromVec(c, node->getValue<SGVec3d>());
    case props::VEC4D:
-        return makeVectorFromVec(c, (*node)->getValue<SGVec4d>());
+        return makeVectorFromVec(c, node->getValue<SGVec4d>());
    default:
        return naNil();
    }
@@ -220,31 +297,39 @@ T makeVecFromVector(naRef vector)
    return vec;
}
+
+// Set the value of a node; returns true if it succeeded or
+// false if it failed. <val> can be a string, number, or a
+// vector or numbers (for SGVec3D/4D types).
+// Forms:
+//    props.Node.setValue(string relative_path,
+//                        val);
+//    props.Node.setValue(val);
static naRef f_setValue(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
+    MOVETARGET(naVec_size(argv) > 1, true);
    naRef val = naVec_get(argv, 0);
    bool result = false;
    if(naIsString(val)) {
-        result = (*node)->setStringValue(naStr_data(val));
+        result = node->setStringValue(naStr_data(val));
    } else if(naIsVector(val)) {
        if(naVec_size(val) == 3)
-            result = (*node)->setValue(makeVecFromVector<SGVec3d>(val));
+            result = node->setValue(makeVecFromVector<SGVec3d>(val));
        else if(naVec_size(val) == 4)
-            result = (*node)->setValue(makeVecFromVector<SGVec4d>(val));
+            result = node->setValue(makeVecFromVector<SGVec4d>(val));
        else
            naRuntimeError(c, "props.setValue() vector value has wrong size");
    } else {
-        naRef n = naNumValue(val);
-        if(naIsNil(n))
+        if(!naIsNum(val))
            naRuntimeError(c, "props.setValue() with non-number");
        double d = naNumValue(val).num;
-        if (osg::isNaN(d)) {
+        if (SGMisc<double>::isNaN(d)) {
          naRuntimeError(c, "props.setValue() passed a NaN");
        }
-        result = (*node)->setDoubleValue(d);
+        result = node->setDoubleValue(d);
    }
    return naNum(result);
}
@@ -252,6 +337,7 @@ static naRef f_setValue(naContext c, naRef me, int argc, naRef* args)
static naRef f_setIntValue(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
+    MOVETARGET(naVec_size(argv) > 1, true);
    // Original code:
    //  int iv = (int)naNumValue(naVec_get(argv, 0)).num;
@@ -263,38 +349,52 @@ static naRef f_setIntValue(naContext c, naRef me, int argc, naRef* args)
    double tmp2 = tmp1.num;
    int iv = (int)tmp2;
-    return naNum((*node)->setIntValue(iv));
+    return naNum(node->setIntValue(iv));
}
static naRef f_setBoolValue(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
+    MOVETARGET(naVec_size(argv) > 1, true);
    naRef val = naVec_get(argv, 0);
-    return naNum((*node)->setBoolValue(naTrue(val) ? true : false));
+    return naNum(node->setBoolValue(naTrue(val) ? true : false));
}
static naRef f_setDoubleValue(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
+    MOVETARGET(naVec_size(argv) > 1, true);
    naRef r = naNumValue(naVec_get(argv, 0));
    if (naIsNil(r))
        naRuntimeError(c, "props.setDoubleValue() with non-number");
-    if (osg::isNaN(r.num)) {
+    if (SGMisc<double>::isNaN(r.num)) {
      naRuntimeError(c, "props.setDoubleValue() passed a NaN");
    }
-    return naNum((*node)->setDoubleValue(r.num));
+    return naNum(node->setDoubleValue(r.num));
}
+
+// Get the parent of this node as a ghost.
+// Forms:
+//    props.Node.getParent();
static naRef f_getParent(naContext c, naRef me, int argc, naRef* args)
{
    NODENOARG();
-    SGPropertyNode* n = (*node)->getParent();
+    SGPropertyNode* n = node->getParent();
    if(!n) return naNil();
    return propNodeGhostCreate(c, n);
}
+
+// Get a child by name and optional index=0, creating if specified (by default it
+// does not create it). If the node does not exist and create is false, then it
+// returns nil, else it returns a (possibly new) property ghost.
+// Forms:
+//    props.Node.getChild(string relative_path,
+//                        int index=0,
+//                        bool create=false);
static naRef f_getChild(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
@@ -304,10 +404,10 @@ static naRef f_getChild(naContext c, naRef me, int argc, naRef* args)
    bool create = naTrue(naVec_get(argv, 2)) != 0;
    SGPropertyNode* n;
    try {
-        if(naIsNil(idx) || !naIsNum(idx)) {
-            n = (*node)->getChild(naStr_data(child), create);
+        if(naIsNil(idx)) {
+            n = node->getChild(naStr_data(child), create);
        } else {
-            n = (*node)->getChild(naStr_data(child), (int)idx.num, create);
+            n = node->getChild(naStr_data(child), (int)idx.num, create);
        }
    } catch (const string& err) {
        naRuntimeError(c, (char *)err.c_str());
@@ -317,21 +417,26 @@ static naRef f_getChild(naContext c, naRef me, int argc, naRef* args)
    return propNodeGhostCreate(c, n);
}
+
+// Get all children with a specified name as a vector of ghosts.
+// Forms:
+//    props.Node.getChildren(string relative_path);
+//    props.Node.getChildren(); #get all children
static naRef f_getChildren(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
    naRef result = naNewVector(c);
    if(naIsNil(argv) || naVec_size(argv) == 0) {
        // Get all children
-        for(int i=0; i<(*node)->nChildren(); i++)
-            naVec_append(result, propNodeGhostCreate(c, (*node)->getChild(i)));
+        for(int i=0; i<node->nChildren(); i++)
+            naVec_append(result, propNodeGhostCreate(c, node->getChild(i)));
    } else {
        // Get all children of a specified name
        naRef name = naVec_get(argv, 0);
        if(!naIsString(name)) return naNil();
        try {
            vector<SGPropertyNode_ptr> children
-                = (*node)->getChildren(naStr_data(name));
+                = node->getChildren(naStr_data(name));
            for(unsigned int i=0; i<children.size(); i++)
                naVec_append(result, propNodeGhostCreate(c, children[i]));
        } catch (const string& err) {
@@ -342,6 +447,12 @@ static naRef f_getChildren(naContext c, naRef me, int argc, naRef* args)
    return result;
}
+
+// Append a named child at the first unused index...
+// Forms:
+//    props.Node.addChild(string name,
+//                        int min_index=0,
+//                        bool append=true);
static naRef f_addChild(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
@@ -353,14 +464,14 @@ static naRef f_addChild(naContext c, naRef me, int argc, naRef* args)
    try
    {
      int min_index = 0;
-      if( !naIsNil(ref_min_index) && naIsNum(ref_min_index) )
+      if(!naIsNil(ref_min_index))
        min_index = ref_min_index.num;
      bool append = true;
-      if( !naIsNil(ref_append) )
+      if(!naIsNil(ref_append))
        append = naTrue(ref_append) != 0;
-      n = (*node)->addChild(naStr_data(child), min_index, append);
+      n = node->addChild(naStr_data(child), min_index, append);
    }
    catch (const string& err)
    {
@@ -387,15 +498,15 @@ static naRef f_addChildren(naContext c, naRef me, int argc, naRef* args)
      count = ref_count.num;
      int min_index = 0;
-      if( !naIsNil(ref_min_index) && naIsNum(ref_min_index) )
+      if(!naIsNil(ref_min_index))
        min_index = ref_min_index.num;
      bool append = true;
-      if( !naIsNil(ref_append) )
+      if(!naIsNil(ref_append))
        append = naTrue(ref_append) != 0;
      const simgear::PropertyList& nodes =
-        (*node)->addChildren(naStr_data(child), count, min_index, append);
+        node->addChildren(naStr_data(child), count, min_index, append);
      naRef result = naNewVector(c);
      for( size_t i = 0; i < nodes.size(); ++i )
@@ -410,36 +521,47 @@ static naRef f_addChildren(naContext c, naRef me, int argc, naRef* args)
    return naNil();
}
+
+// Remove a child by name and index. Returns it as a ghost.
+// Forms:
+//    props.Node.removeChild(string relative_path,
+//                          int index);
static naRef f_removeChild(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
    naRef child = naVec_get(argv, 0);
    naRef index = naVec_get(argv, 1);
    if(!naIsString(child) || !naIsNum(index)) return naNil();
-    SGPropertyNode_ptr n = 0;
+    SGPropertyNode_ptr n;
    try {
-        n = (*node)->removeChild(naStr_data(child), (int)index.num, false);
+        n = node->removeChild(naStr_data(child), (int)index.num);
    } catch (const string& err) {
        naRuntimeError(c, (char *)err.c_str());
    }
    return propNodeGhostCreate(c, n);
}
+
+// Remove all children with specified name. Returns a vector of all the nodes
+// removed as ghosts.
+// Forms:
+//    props.Node.removeChildren(string relative_path);
+//    props.Node.removeChildren(); #remove all children
static naRef f_removeChildren(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
    naRef result = naNewVector(c);
    if(naIsNil(argv) || naVec_size(argv) == 0) {
        // Remove all children
-        for(int i = (*node)->nChildren() - 1; i >=0; i--)
-            naVec_append(result, propNodeGhostCreate(c, (*node)->removeChild(i)));
+        for(int i = node->nChildren() - 1; i >=0; i--)
+            naVec_append(result, propNodeGhostCreate(c, node->removeChild(i)));
    } else {
        // Remove all children of a specified name
        naRef name = naVec_get(argv, 0);
        if(!naIsString(name)) return naNil();
        try {
            vector<SGPropertyNode_ptr> children
-                = (*node)->removeChildren(naStr_data(name), false);
+                = node->removeChildren(naStr_data(name));
            for(unsigned int i=0; i<children.size(); i++)
                naVec_append(result, propNodeGhostCreate(c, children[i]));
        } catch (const string& err) {
@@ -450,36 +572,67 @@ static naRef f_removeChildren(naContext c, naRef me, int argc, naRef* args)
    return result;
}
+// Remove all children of a property node.
+// Forms:
+//    props.Node.removeAllChildren();
+static naRef f_removeAllChildren(naContext c, naRef me, int argc, naRef* args)
+{
+  NODENOARG();
+  node->removeAllChildren();
+  return propNodeGhostCreate(c, node);
+}
+
+// Alias this property to another one; returns 1 on success or 0 on failure
+// (only applicable to tied properties).
+// Forms:
+//    props.Node.alias(string global_path);
+//    props.Node.alias(prop_ghost node);
+//    props.Node.alias(props.Node node); #added by props.nas
static naRef f_alias(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
    SGPropertyNode* al;
    naRef prop = naVec_get(argv, 0);
    try {
- #if 0 // FIXME
+ #if 0
        if(naIsString(prop)) al = globals->get_props()->getNode(naStr_data(prop), true);
-        else if(naIsGhost(prop)) al = *(SGPropertyNode_ptr*)naGhost_ptr(prop);
+        else if(naIsGhost(prop)) al = static_cast<SGPropertyNode*>(naGhost_ptr(prop));
        else throw string("props.alias() with bad argument");
#endif
    } catch (const string& err) {
        naRuntimeError(c, (char *)err.c_str());
        return naNil();
    }
-    return naNum((*node)->alias(al));
+    return naNum(node->alias(al));
}
+
+// Un-alias this property. Returns 1 on success or 0 on failure (only
+// applicable to tied properties).
+// Forms:
+//    props.Node.unalias();
static naRef f_unalias(naContext c, naRef me, int argc, naRef* args)
{
    NODENOARG();
-    return naNum((*node)->unalias());
+    return naNum(node->unalias());
}
+
+// Get the alias of this node as a ghost.
+// Forms:
+//    props.Node.getAliasTarget();
static naRef f_getAliasTarget(naContext c, naRef me, int argc, naRef* args)
{
    NODENOARG();
-    return propNodeGhostCreate(c, (*node)->getAliasTarget());
+    return propNodeGhostCreate(c, node->getAliasTarget());
}
+
+// Get a relative node. Returns nil if it does not exist and create is false,
+// or a ghost object otherwise (wrapped into a props.Node object by props.nas).
+// Forms:
+//    props.Node.getNode(string relative_path,
+//                      bool create=false);
static naRef f_getNode(naContext c, naRef me, int argc, naRef* args)
{
    NODEARG();
@@ -488,7 +641,7 @@ static naRef f_getNode(naContext c, naRef me, int argc, naRef* args)
    if(!naIsString(path)) return naNil();
    SGPropertyNode* n;
    try {
-        n = (*node)->getNode(naStr_data(path), create);
+        n = node->getNode(naStr_data(path), create);
    } catch (const string& err) {
        naRuntimeError(c, (char *)err.c_str());
        return naNil();
@@ -496,45 +649,136 @@ static naRef f_getNode(naContext c, naRef me, int argc, naRef* args)
    return propNodeGhostCreate(c, n);
}
+
+// Create a new property node.
+// Forms:
+//    props.Node.new();
static naRef f_new(naContext c, naRef me, int argc, naRef* args)
{
    return propNodeGhostCreate(c, new SGPropertyNode());
}
+
+// Get the global root node (cached by props.nas so that it does
+// not require a function call).
+// Forms:
+//    props._globals()
+//    props.globals
static naRef f_globals(naContext c, naRef me, int argc, naRef* args)
{
- #if 0 //FIXME
+    return naNil();
+    #if 0
    return propNodeGhostCreate(c, globals->get_props());
- #endif
+    #endif
+}
+
+#if 0
+##
+# Private function to do the work of setValues().
+# The first argument is a child name, the second a nasal scalar,
+# vector, or hash.
+#
+Node._setChildren = func(name, val) {
+    # print("setChildren call for:",name);
+    var subnode = me.getNode(name, 1);
+    if(typeof(val) == "scalar") { subnode.setValue(val); }
+    elsif(typeof(val) == "hash") { subnode.setValues(val); }
+    elsif(typeof(val) == "vector") {
+        for(var i=0; i<size(val); i+=1) {
+            var iname = name ~ "[" ~ i ~ "]";
+            me._setChildren(iname, val[i]);
+        }
+    }
+}
+
+
+#endif
+static naRef f_setChildren(naContext c, naRef me, naRef name, naRef value)
+{
+#if 0
+    // subnode = me.getNode(name, 1);
+    for (int i=0;i<naVec_size(keysVector);i++) {
+    // naHash_get(args[1], naRef key, naRef* out);
+    if(naIsScalar(value) || naIsHash(value) ) {
+ // subnode.setValue
+    } // scalar or hash
+    else if(naIsVector(value)) {
+ for(int v=0;v<=naVec_size(value);v++) {
+ //f_setChildren(v, me, naVec_get(c,value,v));
+ }
+    }
+    } // for each key
+#endif
+    return naNil();
+} // setChildren()
+
+
+#if 0
+Node.setValues = func(val) {
+    foreach(var k; keys(val)) { me._setChildren(k, val[k]); }
+}
+#endif
+
+// http://wiki.flightgear.org/Howto:Extend_Nasal
+// TODO: use try/catch naRuntimeError, SG_LOG
+static naRef f_setValues(naContext c, naRef me, int argc, naRef* args)
+{
+#if 0
+    NODEARG();
+
+    naRef hash = naVec_get(argv, 0);
+    // allocate a new vector to hold the keys in the hash
+    naRef keys = naNewVector(c);
+    // copy all keys into the vector
+    naHash_keys(keys, hash);
+    naRef key, value;
+   
+    SG_LOG(SG_NASAL, SG_ALERT, "keys in hash " << naVec_size(keys) );
+
+    for (int i=0;i<naVec_size(keys);i++) {
+    key = naVec_get(keys,i);
+    naHash_get(hash, key, &value);
+   
+    SG_LOG(SG_NASAL, SG_ALERT, "key/value:" << naStr_data(key) <<"/"<<naStr_data(value) );
+    }
+
+    f_setChildren(c, me, hash, keys);
+#endif
+    return naNil();
}
+
+
static struct {
    naCFunction func;
    const char* name;
} propfuncs[] = {
-    { f_getType, "_getType" },
-    { f_getAttribute, "_getAttribute" },
-    { f_setAttribute, "_setAttribute" },
-    { f_getName, "_getName" },
-    { f_getIndex, "_getIndex" },
-    { f_getValue, "_getValue" },
-    { f_setValue, "_setValue" },
-    { f_setIntValue, "_setIntValue" },
-    { f_setBoolValue, "_setBoolValue" },
-    { f_setDoubleValue, "_setDoubleValue" },
-    { f_getParent, "_getParent" },
-    { f_getChild, "_getChild" },
-    { f_getChildren, "_getChildren" },
-    { f_addChild, "_addChild" },
-    { f_addChildren, "_addChildren" },
-    { f_removeChild, "_removeChild" },
-    { f_removeChildren, "_removeChildren" },
-    { f_alias, "_alias" },
-    { f_unalias, "_unalias" },
-    { f_getAliasTarget, "_getAliasTarget" },
-    { f_getNode, "_getNode" },
-    { f_new, "_new" },
-    { f_globals, "_globals" },
+    { f_getType,            "_getType"            },
+    { f_getAttribute,      "_getAttribute"      },
+    { f_setAttribute,      "_setAttribute"      },
+    { f_getName,            "_getName"            },
+    { f_getIndex,          "_getIndex"          },
+    { f_equals,            "_equals"            },
+    { f_getValue,          "_getValue"          },
+    { f_setValue,          "_setValue"          },
+    { f_setIntValue,        "_setIntValue"        },
+    { f_setBoolValue,      "_setBoolValue"      },
+    { f_setDoubleValue,    "_setDoubleValue"    },
+    { f_getParent,          "_getParent"          },
+    { f_getChild,          "_getChild"          },
+    { f_getChildren,        "_getChildren"        },
+    { f_addChild,          "_addChild"          },
+    { f_addChildren,        "_addChildren"        },
+    { f_removeChild,        "_removeChild"        },
+    { f_removeChildren,    "_removeChildren"    },
+    { f_removeAllChildren,  "_removeAllChildren"  },
+    { f_alias,              "_alias"              },
+    { f_unalias,            "_unalias"            },
+    { f_getAliasTarget,    "_getAliasTarget"    },
+    { f_getNode,            "_getNode"            },
+    { f_new,                "_new"                },
+    { f_globals,            "_globals"            },
+    { f_setValues,            "_setValues"        },
    { 0, 0 }
};
</syntaxhighlight>
== Status (01/2013) ==
'''Project stopped for a while. Use [[OpenRadar]] or [[ATC-pie]] instead.'''
The project may be continued in the future by integrating its building blocks into fgfs mainline, using the new [[Radio Propagation]] framework:
{{cquote|I've seen a couple of external radar clients for Flightgear being developed
right now.
I think that these days, with the advent of Canvas and other improvements, it
should be possible and desirable to have a realistic radar station inside
Flightgear.
At the moment, I'm only concerned about radar ranges and interference of
terrain obstructions, weather, ground clutter.
For this purpose, I propose a template implementation of radar transceivers
and transponders:
It is not possible to provide a detailed terrain picture on every antenna
rotation. Sampling all terrrain 360 degrees around the station would cripple
performance. Thus, I would just take all positions of aircraft inside the
nominal range and perform radio calculations on them, using larger sampling
distances and simpler routines for aircraft above a treshhold flightlevel.
If terrain around the antenna is obstructing the signal, or weather affects
it, we can simulate that correctly.
Transponder responses are also a candidate for the new radio code. Like in
reality, it is possible to have radar contact but lose transponder id because
of radio issues.<ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg38839.html |title=Implementing realistic radar inside the Flightgear engine|author=Adrian Musceac |date= Tue, 27 Nov 2012 05:03:46 -0800}}</ref>|Adrian Musceac}}
<references/>
== Description ==
'''FGRadar''' is a free, open-source, multi-platform and stand-alone [[air traffic control]] (ATC) client for [[FlightGear]] currently under development (10/2012). It makes use of the [[SimGear]] library and the Nasal scripting language.
'''FGRadar''' is a free, open-source, multi-platform and stand-alone [[air traffic control]] (ATC) client for [[FlightGear]] currently under development (10/2012). It makes use of the [[SimGear]] library and the Nasal scripting language.


On the scripting side, it uses the GTK+ and Cairo wrappers for [[Nasal]] in order to perform GUI rendering and drawing operations, respectively.
On the scripting side, it uses the GTK+ and Cairo wrappers for [[Nasal]] in order to perform GUI rendering and drawing operations, respectively.


== Status (10/2012) ==
Under development as of October 2012 (no public release yet):
Under development as of October 2012 (no public release yet):


Line 45: Line 886:
* Add a --data= parameter to specify the location of the data folder (Nasal scripts etc) {{Done}}
* Add a --data= parameter to specify the location of the data folder (Nasal scripts etc) {{Done}}


=== Milestone 3 {{Pending}} ===
=== Milestone 3 {{Done}} 20.10.2012 ===
* finish the merge of the scripting branch into master {{Progressbar|60}}
* fix the Nasal timer implementation after merge into master {{Done}}
* finish the SGApplication template and move stuff from FGRadarApplication/FGPanel {{Progressbar|70}}
* use the --data= parameter in master {{done}}
* SGApplication: make program name configurable {{Done}}
* SGApplication: make --data directory parameter configurable {{Done}}
* SGApplication: make version file name configurable {{Done}}
* SGApplication: add checkVersion() method {{Done}}
 


=== Milestone 4 ===
=== Milestone 4 ===
* re-add the original FGMS/Telnet code from FGRadar for testing purposes
* finish the merge of the scripting branch into master {{done}}
 
* finish the SGApplication template and move stuff from FGRadarApplication/FGPanel {{done}}
* re-add the original FGMS/Telnet code from FGRadar for testing purposes {{Pending}}


=== Milestone x ===
=== Milestone x ===
Line 75: Line 922:


If someone wants to join the project, give ideas, requests or opinions, feel free to do so.  
If someone wants to join the project, give ideas, requests or opinions, feel free to do so.  
The repository is here: https://gitorious.org/fgradar
The archived repository is at: {{gitorious source|proj=fgradar|repo=fgradar|full=1}}


To get commit privileges, please get in touch with Icecode GL via the forum or directly via gitorious.
For an alternative github clone visit {{github source|user=hamzaalloush|repo=fgradar-clone|text=fgradar-clone}}


== Design ==
== Design ==
Line 86: Line 933:
* Multi-Platform support
* Multi-Platform support
* Uses [[Developing using CMake|cmake]] as the build system
* Uses [[Developing using CMake|cmake]] as the build system
* Uses [[Nasal]] based unit tests
* favors the use of smart pointers for memory management to reduce the danger of memory leaks
* favors the use of smart pointers for memory management to reduce the danger of memory leaks
* uses the [[SimGear]] library
* uses the [[SimGear]] library
Line 104: Line 952:
* start using the property-tree {{done}}
* start using the property-tree {{done}}
* start using the SimGear-based logging facilities (SG_LOG) {{done}}
* start using the SimGear-based logging facilities (SG_LOG) {{done}}
* add a new SGSubsystem that connects to fgms and gets a list of aircraft, i.e. via http://mpserver01.flightgear.org:5001/ and SimGear's [https://gitorious.org/fg/simgear/blobs/next/simgear/io/httpget.cxx HTTPRequest] {{Done}}
* add a new SGSubsystem that connects to fgms and gets a list of aircraft, i.e. via http://mpserver01.flightgear.org:5001/ and SimGear's {{simgear source|path=simgear/io/httpget.cxx|text=HTTPRequest}} {{Done}}
* add a "data" folder for scripts etc (fg-root), needs to be installed via CMake {{Done}}
* add a "data" folder for scripts etc (fg-root), needs to be installed via CMake {{Done}}
* use simgear::ResourceManager::instance()->addBasePath(fg_root, simgear::ResourceManager::PRIORITY_DEFAULT) for data folder access {{Done}}
* use simgear::ResourceManager::instance()->addBasePath(fg_root, simgear::ResourceManager::PRIORITY_DEFAULT) for data folder access {{Done}}
* merge Nasal progress from initial-scripting-support branch into master {{Done}}




Line 118: Line 967:
* support the FG performance monitor
* support the FG performance monitor
* add a dedicated memory management subsystem that tracks RAM usage per subsystem
* add a dedicated memory management subsystem that tracks RAM usage per subsystem
* support dynamic subsystem initialization (see FG [https://gitorious.org/fg/flightgear/commit/8608a480736651999c5ea31a489343ee097ee915] )
* support dynamic subsystem initialization (see {{flightgear source|commit=8608a4|view=commit}} )


== Known Issues ==
== Known Issues ==


=== Regressions while merging the scripting branch ==
=== Regressions while merging the scripting branch ===


* data folder support using the simgear::resourceManager (used by Nasal)
* fix the realtime property so that the events mgr can use it
* data folder support using the simgear::resourceManager (used by Nasal) {{Done}}
* global property tree handle {{Done}}
* global property tree handle {{Done}}
* use FGPanel's sleep() implementation to fix the busy loop
* use FGPanel's sleep() implementation to fix the busy loop {{done}}
* command line parsing
* command line parsing (--prop: not yet supported!) {{done}}


=== Coding ===
=== Coding ===
* The current '''SGApplication''' implementation uses a "busy waiting"-loop, needs to use the FGRadarApplication/FGPanel implementation {{Pending}}
* Determine if we need to find a better solution for GTK's blocking main loop than jusing doing '''gtk_main_iteration_do()''' (threading? ){{Pending}}
* Determine if we need to find a better solution for GTK's blocking main loop than jusing doing '''gtk_main_iteration_do()''' (threading? ){{Pending}}


Line 141: Line 990:


== Work in Progress ==
== Work in Progress ==
* add FlightGear's NavDB subsystem to FGRadar (Icecode) {{Pending}}
* expose SimGear's SGSocket to Nasal via a separate Nasal ghost, so that Nasal can use sockets (Hooray) {{Progressbar|10}}
* expose SimGear's SGSocket to Nasal via a separate Nasal ghost, so that Nasal can use sockets (Hooray) {{Progressbar|10}}
* improved cross-platform support, so that FGRadar also works on Windows {{Progressbar|50}}
* improved cross-platform support, so that FGRadar also works on Windows {{Progressbar|50}}
* Nasal scripting support (Hooray) {{Progressbar|80}}
* Nasal scripting support (Hooray) {{Progressbar|80}}
* re-add and port submodule support for scripting, so that multiple files can be loaded into the same namespace (Hooray) {{pending}}
* re-add and port submodule support for scripting, so that multiple files can be loaded into the same namespace (Hooray) {{pending}}
* add a '''SG_OSGApplication''' inherited from SGApplication which provides an osgviewer-based window (see [https://gitorious.org/fg/flightgear/blobs/next/utils/fgviewer/fgviewer.cxx $FG_SRC/utils/fgviewer/fgviewer.cxx]) to support Canvas {{Progressbar|20}}
* add a '''SG_OSGApplication''' inherited from SGApplication which provides an osgviewer-based window (see {{flightgear source|path=utils/fgviewer/fgviewer.cxx|pre=$FG_SRC}}) to support Canvas {{Progressbar|20}}
* improved Nasal GTK wrapper (port from GTK 2.0 to 3.x) {{pending}}
* improved Nasal GTK wrapper (port from GTK 2.0 to 3.x) {{pending}}
* An XML configurable GUI using Gtk (glade) {{pending}}
* An XML configurable GUI using Gtk (glade) {{pending}}
* merge Nasal progress from initial-scripting-support branch into master (Icecode) {{Progressbar|70}}
 
== SGApplication ==
* enforce use of smart pointers {{done}}
* use real singletons to manage resource access
* start working on SGScriptableApplication
* support environment variables
* start working on config file support (~/.fgfsrc)
* fix all compiler warnings
* add DoxyGen comments


== Canvas Integration ==
== Canvas Integration ==
Line 159: Line 1,017:
* For initializing the Nasal support it would be good to have support for the same kind of initialization as it's currently used in the fg/Scripting files.
* For initializing the Nasal support it would be good to have support for the same kind of initialization as it's currently used in the fg/Scripting files.
* the map layers (airports, towers, runways, taxiways etc) need full navdb access, i.e. the NasalPositioned bindings
* the map layers (airports, towers, runways, taxiways etc) need full navdb access, i.e. the NasalPositioned bindings
Moving the Canvas to SG should be finished for now. Basically we need something like the canvas_mgr (Only the constructor and something like GUIMgr::addPlacement is needed) and FGCanvasSystemAdapter. Everything else should now be part of SimGear.


== The Scripting System ==
== The Scripting System ==
Line 179: Line 1,040:


Each script in any of these two folders is loaded into a namespace based on the file name - which is identical to FlightGear. However, the '''lib''' folder is processed prior to the '''app''' folder obviously.
Each script in any of these two folders is loaded into a namespace based on the file name - which is identical to FlightGear. However, the '''lib''' folder is processed prior to the '''app''' folder obviously.
In addition to these two folders, there's a third folder named '''tests''' this includes Nasal-based unit tests that are executed after loading the '''lib''' directory, and prior to running the '''app''' directory.


Note that support for Nasal sub modules (where all Nasal files in a single folder are loaded into the same namespace) hasn't yet been implemented, but will soon be added to make it easier to support the [[Canvas]] scripting layer. That will probably mean another folder in data/Nasal (loaded prior to '''app''', but after '''lib'''), namely:
Note that support for Nasal sub modules (where all Nasal files in a single folder are loaded into the same namespace) hasn't yet been implemented, but will soon be added to make it easier to support the [[Canvas]] scripting layer. That will probably mean another folder in data/Nasal (loaded prior to '''app''', but after '''lib'''), namely:
Line 189: Line 1,052:
* [[Stand Alone ATC Control Development]]
* [[Stand Alone ATC Control Development]]


[[Category:Air Traffic Control]]
[[Category:ATC clients]]
[[Category:Software]]