Howto:Visualizing PropertyList XML using Nasal and Canvas

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.

Motivation

I am currently analysing a structure of a FG plane model. It is of course possible to start from, say, X-club-set.xml and X-glass-set.xml, find out that both include X-base.xml, and then follow recursively all inclusions and references, but it is a tedious process and it is easy to overlook things.

Challenges

The thing is, property tree does not contain information from which file its elements originated. Parsing xml, looking for 'include=' inside a tag, is not complicated, but there are other more specific directives which also refer to other files, for example aero, systems, sound, flight_control.. Looking for a pattern '*.xml' catches most but not all cases, as some files are referenced by name without extension..

And, there are also other relationships besides include, for example 'variant of'. It would be nice to visualise them too.

FG parser/loader knows about all these special cases, but there does not seem to be a way of telling it to describe the structure.. Or.. is there?[1]

Nasal/Canvas is a neat option, but like you said, really only if you already know what to look for - if you don't know what to look for (attributes/tags etc), the best option would be either patching SG/FG in C++ space, or using a hooking mechanism like strace, in conjunction with a Python script to track file system accesses and make the script "reverse engineer" relevant tags by looking for tags that trigger read accesses. This sort of thing is easy to do on Linux, in general however, patching SG/FG to "annotate" a property tree with meta attributes recursively would be the lowest hanging fruit IMO.[2]

Question

Is there a tool, maybe a Doxygen config, which, given a -set.xml file as input will produce an UML-like diagram of relationships between various files in a model?[3]

Idea

You could use Nasal, analyze the top level set file and then process all include directives recursively.

That way, the whole structure could be visualized using the canvas.

If I were to do this, I would use a tree view like approach and simply use different images for different file types.

I suppose, something like that could be prototyped in under 100 lines of code[4]

If you only want to parse PropertyList/XML files, you can directly use the readProperties API - for generic XML handling, see io.nas

Again, I suppose we could prototype a simple Nasal module to do most of this fairly easily.

And we could use a canvas and some Svg artwork to visualize everything

Besides, over the years, a number of devs have been wanting to come up with a "validation" mode for aircraft devs - what you're now working on, could become the foundation for something along these lines[5]

Getting started

Playground

var (width, height) = (800,600);

# for prototyping purposes, we are going to fetch imagery from wiki commons
var URLContainer = {
# https://commons.wikimedia.org/w/index.php?search=folder+&title=Special:MediaSearch&go=Go&type=image
folder: 'https://upload.wikimedia.org/wikipedia/commons/7/74/Folder-dynamic-color.png',
};

var filename = "/Aircraft/c172p/c172p-set.xml";
var path = getprop("/sim/fg-root") ~ filename;
print("Path:", path);
var blob = io.read_properties(path).getValues();


var title = "-set.XMLVisualizer: " ~ filename; #~'('~blob['flight-model']~')';

# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)
var window = canvas.Window.new([width,height],"dialog").set('title',title);

# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));

# Using specific css colors would also be possible:
# myCanvas.set("background", "#ffaac0");

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

var vbox = canvas.VBoxLayout.new();
myCanvas.setLayout(vbox);

var scroll = canvas.gui.widgets.ScrollArea.new(root, canvas.style, {size: [96, 128]}).move(20, 100);
vbox.addItem(scroll, 1);

var scrollContent =
      scroll.getContent()
            .set("font", "LiberationFonts/LiberationSans-Bold.ttf")
            .set("character-size", 16)
            .set("alignment", "left-center");

var list = canvas.VBoxLayout.new();
scroll.setLayout(list);

var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); 
label.setImage(URLContainer.folder);
label.setFixedSize(256,256);
list.addItem(label);

debug.dump (blob);

if (0) 
for (var i=1;i<=5;i+=1) {
var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); 
label.setImage("Textures/Splash"~i~".png");
label.setFixedSize(256,256);
list.addItem(label);
}

References

References