Howto:Add new fgcommands to FlightGear: Difference between revisions

Jump to navigation Jump to search
Source and commit templates instead if explicit URLs; +-syntax highlighting: php → nasal
m (→‎Background: http://forum.flightgear.org/viewtopic.php?f=4&t=24198&p=261526#p261526)
(Source and commit templates instead if explicit URLs; +-syntax highlighting: php → nasal)
Line 8: Line 8:
So there is a certain marshaling/conversion overhead involved here. While this is certainly negligible for simple commands that are run rarely, this overhead may add up once a certain fgcommand gets called repeatedly at a high rate. In such cases it may be more efficient to let the command work with argument lists (vectors) rather than just a single argument, so that multiple tasks can be completed by a single invocation.
So there is a certain marshaling/conversion overhead involved here. While this is certainly negligible for simple commands that are run rarely, this overhead may add up once a certain fgcommand gets called repeatedly at a high rate. In such cases it may be more efficient to let the command work with argument lists (vectors) rather than just a single argument, so that multiple tasks can be completed by a single invocation.


An overview of fgcommands currently available and supported is provided in [http://gitorious.org/fg/fgdata/blobs/master/Docs/README.commands $FG_ROOT/Docs/README.commands].
An overview of fgcommands currently available and supported is provided in {{readme file|commands}}.


Beginning with FlightGear 2.11+, new fgcommands can now also be implemented in [[Nasal]] and registered through new Nasal extension functions:
Beginning with FlightGear 2.11+, new fgcommands can now also be implemented in [[Nasal]] and registered through new Nasal extension functions:
Line 16: Line 16:
This allows you to make up new fgcommands just by implementing them in Nasal:
This allows you to make up new fgcommands just by implementing them in Nasal:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
addcommand("hello", func {
addcommand("hello", func {
  print("hello");
  print("hello");
Line 25: Line 25:
to process arguments, you can pass a property tree with nodes:
to process arguments, you can pass a property tree with nodes:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
addcommand("hello", func(node) {
addcommand("hello", func(node) {
  print("hello ", node.getNode('name').getValue() );
  print("hello ", node.getNode('name').getValue() );
Line 32: Line 32:
</syntaxhighlight>
</syntaxhighlight>


Obviously, not all fgcommands can be implemented in Nasal, which is why you may sometimes still have to use C++ - creating a new fgcommand using C++ is also fairly simple actually. The source code you need to look at is in [http://gitorious.org/fg/flightgear/blobs/next/src/Main/fg_commands.cxx $FG_SRC/Main/fg_commands.cxx].
Obviously, not all fgcommands can be implemented in Nasal, which is why you may sometimes still have to use C++ - creating a new fgcommand using C++ is also fairly simple actually. The source code you need to look at is in {{flightgear file|src/Main/fg_commands.cxx}}.


{{FGCquote
{{FGCquote
Line 46: Line 46:
}}
}}


All commands are listed there, beginning in [http://gitorious.org/fg/flightgear/blobs/next/src/Main/fg_commands.cxx#line160 line #160]. The signature of these callback functions is always such that they:
All commands are listed there, beginning in {{flightgear file|src/Main/fg_commands.cxx|l=160|t=line 160}}. The signature of these callback functions is always such that they:


* have static linkage
* have static linkage
Line 54: Line 54:
{{IO Restrictions}}
{{IO Restrictions}}


The simplest command can be found in [http://gitorious.org/fg/flightgear/blobs/next/src/Main/fg_commands.cxx#line167 line 167], the do_null() command which does nothing:
The simplest command can be found from {{flightgear file|src/Main/fg_commands.cxx|l=167|t=line 167}}, the do_null() command which does nothing:


<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
Line 84: Line 84:
Next, you could run the modified fgcommand by using the Nasal console (make sure to watch your console):
Next, you could run the modified fgcommand by using the Nasal console (make sure to watch your console):


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
  fgcommand("null");
  fgcommand("null");
</syntaxhighlight>
</syntaxhighlight>
Line 101: Line 101:
These all need to be first set up in the property tree separately, i.e. using setprop:
These all need to be first set up in the property tree separately, i.e. using setprop:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
  setprop("/temp1/path","my path" );
  setprop("/temp1/path","my path" );
  setprop("/temp1/file","filename" );
  setprop("/temp1/file","filename" );
Line 110: Line 110:
You could just as well use a temporary property tree object, rather than the global property tree:
You could just as well use a temporary property tree object, rather than the global property tree:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
  var myTree = props.Node.new( {"filename": "stars.jpeg"} );
  var myTree = props.Node.new( {"filename": "stars.jpeg"} );
  fgcommand("null", myTree);
  fgcommand("null", myTree);
Line 117: Line 117:
This would turn the hash initialized into a property tree structure, an even shorter version would be:
This would turn the hash initialized into a property tree structure, an even shorter version would be:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
  fgcommand("null", props.Node.new( {"filename": "stars.jpeg"} ) );
  fgcommand("null", props.Node.new( {"filename": "stars.jpeg"} ) );
</syntaxhighlight>
</syntaxhighlight>
Line 123: Line 123:
In general, using a temporary property tree object is preferable and recommended if you don't need to access the data in the global property tree otherwise.
In general, using a temporary property tree object is preferable and recommended if you don't need to access the data in the global property tree otherwise.


So, what the C++ code is doing once it is called, is getting the arguments out of the property argument, see [http://gitorious.org/fg/flightgear/blobs/next/src/Main/fg_commands.cxx#line1183 line 1183]:
So, what the C++ code is doing once it is called, is getting the arguments out of the property argument, see {{flightgear file|src/Main/fg_commands.cxx|l=1183|t=line 1183}}:


<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
Line 171: Line 171:


== Exception handling ==
== Exception handling ==
To be on the safe side, complex fgcommands, which may terminate for one reason or another (such as missing files), should make use of exception handling to catch exceptions and deal with them gracefully, so that they cannot affect the simulator critically.  
To be on the safe side, complex fgcommands, which may terminate for one reason or another (such as missing files), should make use of exception handling to catch exceptions and deal with them gracefully, so that they cannot affect the simulator critically.  


This is accomplished by using standard C++ try/catch blocks, usually catching an sg_exception. The SG_LOG() macro can be used to print debugging information to the console. If you'd like to display error information using the native FlightGear GUI, you can use the "guiErrorMessage(const char*,sg_exception)" helper.
This is accomplished by using standard C++ try/catch blocks, usually catching an sg_exception. The SG_LOG() macro can be used to print debugging information to the console. If you'd like to display error information using the native FlightGear GUI, you can use the "guiErrorMessage(const char*,sg_exception)" helper.


For example, see the callback function "do_preferences_load" in [http://gitorious.org/fg/flightgear/blobs/next/src/Main/fg_commands.cxx#line445 line 445]:
For example, see the callback function "do_preferences_load" from {{flightgear file|src/Main/fg_commands.cxx|l=445|t=line 445}}:


<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
Line 202: Line 201:


== Hello World ==
== Hello World ==
For example, to implement a new "hello_world" command, that accepts a single parameter named "name":
For example, to implement a new "hello_world" command, that accepts a single parameter named "name":


Line 234: Line 232:
After adding the new callback to the callback list and rebuilding FlightGear, you can easily invoke the new "hello_world" fgcommand using the Nasal console like this:
After adding the new callback to the callback list and rebuilding FlightGear, you can easily invoke the new "hello_world" fgcommand using the Nasal console like this:


<syntaxhighlight lang="php">
<syntaxhighlight lang="nasal">
  fgcommand("hello_world", props.Node.new( {"name": "Howard Hughes"} ));
  fgcommand("hello_world", props.Node.new( {"name": "Howard Hughes"} ));
</syntaxhighlight>
</syntaxhighlight>
Line 240: Line 238:


= Adding new commands =
= Adding new commands =
All new commands must have the previously described signature, the functions should then be added to the list of built-in commands, beginning in line [http://gitorious.org/fg/flightgear/blobs/next/src/Main/fg_commands.cxx#line1552 1552]. The list of built-in commands maps the human-readable names used in README.commands to the names of the internal C++ functions implementing them.
All new commands must have the previously described signature, the functions should then be added to the list of built-in commands, beginning in {{flightgear file|src/Main/fg_commands.cxx|l=1552|t=line 1552}}. The list of built-in commands maps the human-readable names used in README.commands to the names of the internal C++ functions implementing them.


== Reaching out to subsystems ==
== Reaching out to subsystems ==
Quite possibly, you may need to reach out to some other subsystems to implement a certain fgcommand, such as accessing the sound system, the networking/multiplayer system or the FDM system, this is accomplished by using the <code>globals-></code> pointer, which allows you to access other subsystems easily.  
Quite possibly, you may need to reach out to some other subsystems to implement a certain fgcommand, such as accessing the sound system, the networking/multiplayer system or the FDM system, this is accomplished by using the <code>globals-></code> pointer, which allows you to access other subsystems easily.  


Please refer to [http://gitorious.org/fg/flightgear/blobs/next/src/Main/globals.cxx#line120 $FG_SRC/Main/globals.cxx] and its header file [http://gitorious.org/fg/flightgear/blobs/next/src/Main/globals.hxx globals.hxx]. A number of helpful usage examples can be found by searching fg_commands.cxx for "globals->".
Please refer to {{flightgear file|src/Main/globals.cxx|l=120}} and its header file {{flightgear file|src/Main/globals.hxx|t=globals.hxx}}. A number of helpful usage examples can be found by searching fg_commands.cxx for "globals->".


In general, you will always want to add a NULL pointer check to ensure that the corresponding subsystem handle could actually be retrieved:
In general, you will always want to add a NULL pointer check to ensure that the corresponding subsystem handle could actually be retrieved:
Line 260: Line 257:
Equally, it may be appropriate to wrap code in between try/catch blocks, too.
Equally, it may be appropriate to wrap code in between try/catch blocks, too.


A list of methods available to access certain subsystems is provided here in [http://gitorious.org/fg/flightgear/blobs/next/src/Main/globals.hxx#line220 globals.hxx, beginning in line 220].
A list of methods available to access certain subsystems is provided here in {{flightgear file|src/Main/globals.hxx|l=220|t=globals.hxx, beginning in line 220}}.


== Subsystem specific fgcommands ==
== Subsystem specific fgcommands ==
Line 267: Line 264:
fgcommands having implicit dependencies on other subsystems, should not be added in a hard-coded fashion, but via the ctor/init or postinit() methods of the corresponding subsystem itself - otherwise, such code is conflicting with James' reset/-reinit work - in particular, the very fgcommands intended to allow subsystems to be shut-down and re-initialized.
fgcommands having implicit dependencies on other subsystems, should not be added in a hard-coded fashion, but via the ctor/init or postinit() methods of the corresponding subsystem itself - otherwise, such code is conflicting with James' reset/-reinit work - in particular, the very fgcommands intended to allow subsystems to be shut-down and re-initialized.


In particular see this commit [https://gitorious.org/fg/flightgear/commit/8608a480736651999c5ea31a489343ee097ee915].
In particular, see {{flightgear commit|8608a480}}.


Even if such additions don't break FG immediately, they contribute to crippling the reset/re-init effort - and will make it increasingly difficult to allow run-time dependencies to be better established and formalized.
Even if such additions don't break FG immediately, they contribute to crippling the reset/re-init effort - and will make it increasingly difficult to allow run-time dependencies to be better established and formalized.


We've had a long discussion about this exact issue WRT to Nasal/CppBind bindings added unconditionally, which is causing the same problem. The general consensus was that subsystem-specific functionality must be initialized and cleaned up by the corresponding subsystem itself, instead of always assuming that all subsystems are available.
We've had a long discussion about this exact issue WRT to Nasal/CppBind bindings added unconditionally, which is causing the same problem. The general consensus was that subsystem-specific functionality must be initialized and cleaned up by the corresponding subsystem itself, instead of always assuming that all subsystems are available.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#include <simgear/structure/commands.hxx>
#include <simgear/structure/commands.hxx>
Line 303: Line 301:
</pre>
</pre>


=== removing fgcommands ===
=== Removing fgcommands ===
Equally, there's an API for removing fgcommands on demand, e.g. when shutting down a subsystem - which would be typically done inside your SGSubsystem's dtor:
Equally, there's an API for removing fgcommands on demand, e.g. when shutting down a subsystem - which would be typically done inside your SGSubsystem's dtor:


Line 318: Line 316:


== Finally ==
== Finally ==
When you send patches or file merge requests for new fgcommands, please also make sure to send patches for README.commands, too - so that your new commands are documented there. Otherwise, people may have a hard time using/maintaining your fgcommands, possibly years after you added them, without you even being around by then.
When you send patches or file merge requests for new fgcommands, please also make sure to send patches for README.commands, too - so that your new commands are documented there. Otherwise, people may have a hard time using/maintaining your fgcommands, possibly years after you added them, without you even being around by then.


Navigation menu