Howto:Create animation XML files from Nasal: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
mNo edit summary
m (Switch to the {{forum url}} template for all forum links.)
 
(13 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{Stub}}
Objective: Use [[Nasal]] scripting to dynamically create an XML file by using the setprop() API call to set up your XML file's PropertyList-encoded structure and then writing the result to the file system using the write_properties() wrapper found in $FG_ROOT/Nasal/io.nas.
Objective: Use [[Nasal]] scripting to dynamically create an XML file by using the setprop() API call to set up your XML file's PropertyList-encoded structure and then writing the result to the file system using the write_properties() wrapper found in $FG_ROOT/Nasal/io.nas.


There are two wrappers provided: read_properties() and write_properties. These are wrappers for the corresponding fgcommands "load-xml" and "save-xml".
There are two wrappers provided: read_properties() and write_properties. These are wrappers for the corresponding fgcommands loadxml ({{readme file|commands|line=133}}) and save-xml ({{readme file|commands|line=139}}).


So, you don't need to look into the low level XML processing routines, you can directly use these PropertyList-wrappers - these allow you to directly dump a property tree sub branch to an XML file. So you can create your structure dynamically using setprop() and at the end use the write_properties() wrapper to write the resulting XML file to disk. That should save you some time.
So, you don't need to look into the low level XML processing routines, you can directly use these PropertyList-wrappers - these allow you to directly dump a property tree sub branch to an XML file. So you can create your structure dynamically using setprop() and at the end use the write_properties() wrapper to write the resulting XML file to disk. That should save you some time.


{{IO Restrictions}}


== Background ==
== Background ==
Line 13: Line 12:
== Getting started ==
== Getting started ==


The following piece of Nasal creates a new XML file:
The following piece of Nasal creates a new XML file, in the standard FlightGear [[PropertyList XML File|PropertyList]] encoded form:


<syntaxhighlight lang="php">
<syntaxhighlight lang="php">
Line 58: Line 57:
     var filename="xmltest.xml";
     var filename="xmltest.xml";
     setprop(location~"path", "vsd.ac");
     setprop(location~"path", "vsd.ac");
     setprop(location~"animation/type", "select");
     setprop(location~"animation[0]/type", "select");
     setprop(location~"animation/object-name", "vsd");
     setprop(location~"animation[0]/object-name", "vsd");
     setprop(location~"animation/condition/greather-than-equals/property", "systems/electrical/outputs/efis");
     setprop(location~"animation[0]/condition/greather-than-equals/property", "systems/electrical/outputs/efis");
     setprop(location~"animation/condition/greather-than-equals/value", "9");
     setprop(location~"animation[0]/condition/greather-than-equals/value", "9");
     io.write_properties(filename, location);
     io.write_properties(filename, location);
</syntaxhighlight>
</syntaxhighlight>


Note that multiple setprop() calls like this would obviously overwrite the previous animation, so you would need to use explicit indices here:
* animation[0]
* animation[1]
* animation[2]
This also holds true for any child nodes (once you have several identically named nodes):
* object-name[0]
* object-name[1]
* object-name[2]
* object-name[3]
So, it is always a good idea to explicitly add the index, too.
This is identical to how the property tree internally works already: /sim/foo is equivalent to /sim/foo[0]
If you don't understand how this works, please take a look at: {{readme file|xmlpanel|line=162}}
If you want this to be even easier, you could use the props.nas module, because its methods have support for adding indices automatically.


Next, you could add a handful of helpers (or even a class wrapping animations) to make things a bit easier.
Next, you could add a handful of helpers (or even a class wrapping animations) to make things a bit easier.
Line 96: Line 115:
Some additional info is available in $FG_ROOT/Docs:
Some additional info is available in $FG_ROOT/Docs:


* http://gitorious.org/fg/fgdata/blobs/master/Docs/README.xmlpanel#line162
* {{readme file|xmlpanel|line=162}}
* http://gitorious.org/fg/fgdata/blobs/master/Docs/README.xmlpanel#line191
* {{readme file|xmlpanel|line=191}}
 


There is also support for customizing included files (overriding certain properties with custom defaults):
http://gitorious.org/fg/fgdata/blobs/master/Docs/README.xmlpanel#line245


==Related==
There is also support for customizing included files (overriding certain properties with custom defaults): {{readme file|xmlpanel|line=245}}


== External links ==
* {{forum url|t=15226}}
* {{forum url|t=15214}}
* {{forum url|t=15200}}


[[Category:Howto]]
[[Category:Nasal howto]]
[[Category:Nasal]]

Latest revision as of 16:38, 7 June 2019

Objective: Use Nasal scripting to dynamically create an XML file by using the setprop() API call to set up your XML file's PropertyList-encoded structure and then writing the result to the file system using the write_properties() wrapper found in $FG_ROOT/Nasal/io.nas.

There are two wrappers provided: read_properties() and write_properties. These are wrappers for the corresponding fgcommands loadxml ($FG_ROOT/Docs/README.commands#l133) and save-xml ($FG_ROOT/Docs/README.commands#l139).

So, you don't need to look into the low level XML processing routines, you can directly use these PropertyList-wrappers - these allow you to directly dump a property tree sub branch to an XML file. So you can create your structure dynamically using setprop() and at the end use the write_properties() wrapper to write the resulting XML file to disk. That should save you some time.

Note All FlightGear disk I/O handled via Nasal scripting and/or fgcommands, is subject to access validation via IOrules. This includes the SGPath bindings in FlightGear 2.99+

However, unlike $FG_ROOT, $FG_HOME is generally accessible for writing, consider this example:

#  come up with a path and filename in $FG_HOME, inside the Export sub folder, file name is test.xml
var filename = getprop("/sim/fg-home") ~ "/Export/test.xml";

# use the write_properties() helper in io.nas, which is a wrapper for the savexml fgcommand (see README.commands)
io.write_properties( path: filename, prop: "/sim" );

This will dump the sub branch of the /sim property tree into $FG_HOME/Export/test.xml

For additional examples, see $FG_ROOT/Nasal/io.nas

To learn more about PropertyList processing via loadxml and savexml, please see $FG_ROOT/Docs/README.commands

Internally, all read/write access is validated via an API called fgValidatePath(), for details please see $FG_SRC/Main/util.cxx

Background

In general, this shouldn't really be necessary - because XML files are meant to be created and edited manually. On the other hand, due to the lack of a simple 2D drawing API accessible from Nasal, more and more complex "glass cockpit"-style instruments are getting implemented using the XML-based approach. These files (often more than 100-200 kbytes in size) contain fairly repetitive markup that could (and should) be created procedurally in an automated fashion. So, the approach suggested here is to use a separate Nasal script to create the required XML markup dynamically, rather than having to copy/paste and customize hundreds of XML animations manually using a text editor.

Getting started

The following piece of Nasal creates a new XML file, in the standard FlightGear PropertyList encoded form:

 var location= "/temp/test/foo"; # location in the global FG property tree
 var filename="test.xml"; # location in the local file system (will be OVERWRITTEN/DELETED!)
 setprop(location, "hello world");
 io.write_properties(filename, location);


The created output is:

 <?xml version="1.0"?>
 <PropertyList>
  <temp>
   <test>
    <foo>hello world</foo>
   </test>
  </temp>

Once you understand how this works, you can dynamically create PropertyList-encoded XML files for your instruments. Consider the following XML file containing a single animation:

       <path>vsd.ac</path>
       <animation>
          <type>select</type>
          <object-name>vsd</object-name>
          <condition>
             <greater-than-equals>
                <property>systems/electrical/outputs/efis</property>
                <value>9</value>
             </greater-than-equals>
          </condition>
       </animation>

Now, to create this snippet of XML procedurally, you can use this piece of Nasal:

    var location = "/temp/test/";
    var filename="xmltest.xml";
    setprop(location~"path", "vsd.ac");
    setprop(location~"animation[0]/type", "select");
    setprop(location~"animation[0]/object-name", "vsd");
    setprop(location~"animation[0]/condition/greather-than-equals/property", "systems/electrical/outputs/efis");
    setprop(location~"animation[0]/condition/greather-than-equals/value", "9");
    io.write_properties(filename, location);


Note that multiple setprop() calls like this would obviously overwrite the previous animation, so you would need to use explicit indices here:

  • animation[0]
  • animation[1]
  • animation[2]

This also holds true for any child nodes (once you have several identically named nodes):

  • object-name[0]
  • object-name[1]
  • object-name[2]
  • object-name[3]

So, it is always a good idea to explicitly add the index, too.

This is identical to how the property tree internally works already: /sim/foo is equivalent to /sim/foo[0] If you don't understand how this works, please take a look at: $FG_ROOT/Docs/README.xmlpanel#l162

If you want this to be even easier, you could use the props.nas module, because its methods have support for adding indices automatically.

Next, you could add a handful of helpers (or even a class wrapping animations) to make things a bit easier. Anybody interested in coming up with an OOP wrapper for this, should look into using the props.nas module available in $FG_ROOT/Nasal.

If you already have some prefabricated XML file that you'd like to continue using, then you could also just automate the creation of certain animations and save them in a separate file. This is supported by including a PropertyList file in another file using the "include" attribute:

 <PropertyList>
  <animation include="vsd.xml"/>
 </PropertyList>


 <?xml version="1.0"?>
  <PropertyList>
   <foo include="someotherfile.xml"/>
  </PropertyList>
<?xml version="1.0"?>
<PropertyList>
    <bla>data</bla>
</PropertyList>

This would result in /foo/bla=data

Some additional info is available in $FG_ROOT/Docs:


There is also support for customizing included files (overriding certain properties with custom defaults): $FG_ROOT/Docs/README.xmlpanel#l245

External links