Sunteți pe pagina 1din 10

Writing xml Gauges for FS-2004

Renato C. PROTTI

What is xml ?
xml stands for eXtensible Markup Language.
By analogy to HTML and some other languages, xml is mostly used to "describe" text.
This means that the only function of xml is to describe (markup)
Instead of describing how data has to display (the way data looks) like HTML, XML describes how data
just is (not the way it looks)!
see W3C - World Wide Web Consortium for more info on html, xml , ...

How do Gauges work under Flight Simulator ?


FS always uses gauges to display aircraft and flight parameters.
Gauges are those flight deck instruments with needles, moving strips, rolling drums, etc
and the recently developed instruments are glass panels (screen-displays) ...
FS has a mechanism of calling external libraries of routines (.dll) doing the job of displaying the gauge
and components.
So in the legacy panels (and some new ones) the gauge is a program located in a specific directory
(FS9\Gauges) with an extension .gau
This .gau is in fact a .dll but specialized for FS to handle a gauge.
FS is able to call this program when it founds the definition in the panel description file.
Gauges programs themselves are written to be able to call internal FS functions to get objects displayed
on the panel.
All these functions (gauges macros) are described in the Panel SDK document.
As we can see, every gauge element is related to a macro written to drive its behaviour.
Note : Beside the Panel SDK, it's useful to read the Aircraft Container SDK, we'll see later how to build
panels using our gauges.
Another way to learn how it works and which are the words, is to look inside some *.dll of FS
(CONTROLS.DLL - MAIN.DLL - PANELS.DLL - SIM1.dll and others )

Example:
MAKE_NEEDLE
When called, this macro will receive as parameters :
Background_Bitmap and centrepoint target position, needle_Bitmap and rotation_point position, Variable
for rotation angle calculation, ... and some flags to drive details.
Using the parameters, FS will display the background, place the needle_bitmap with rotation_point
corresponding to the centrepoint and then start rotating this needle_bitmap the amount represented by
the associated variable.
All the variables (Parameters) are described in the Parameters.doc of Panel SDK.
Note that some are read only and some can be altered through usage of Events.
Events are internal functions used to modify the FS-Variables.
The modifications are made via the Events and not directly on the variable, this is to ensure FS keeps
control over the variable values and avoids inconsistencies.

So what about xml ?

As we've seen, xml is a language used to describe data.


FS allows external programs to call internal macros giving parameters.

Comments Welcome to Renato.Protti@Skynet.be

1/10

Writing xml Gauges for FS-2004


Renato C. PROTTI
Now : What if FS would carry a special mode in which it is itself able to directly call the internal macros
just by finding the corresponding set of parameters and the macro-id ?
By using special xml tags it's really simple to describe the data to be transmitted to the macro isn't it ?
Yes, and that's how it works ...
Example:
<Element>
<Position X="123" Y="115"/>
<Image Name="Needle.bmp" Bright="Yes" PointsTo="East"
ImageSizes="25,5,25,1">
<Axis X="0" Y="3"/>
</Image>
<Rotate>
<Value Minimum="0" Maximum="928"> (A:TURB ENG2 ITT, Celsius) abs 0 max 928 min
</Value>
<Nonlinearity>
<Item Value="0" Degrees="0"/>
<Item Value="928" Degrees="230"/>
</Nonlinearity>
</Rotate>
</Element>
When a tag <Element> is encountered, it means : "here starts a new set of data", a new object.
Then FS prepares to call a new macro : <Image> as stated by the next tag, ...
Then the image is <Rotate>d according to (A:TURB ENG2 ITT, Celsius)
So we just need to write groups of definitions included between
<Element>
and
</Element>
Telling how this element (object) has to be handled by FS internal macro-set.
We tell FS how to handle the object by using other tags:
<Image>, <Text>, <String>, <Value> ...
and the behaviour with <Rotate>, <Shift>, <Scale>, <Clip> ...
We'll see all these tags in details later on

How to work with xml files - Use an xml editor


My choice was CookTop by Viktor PAVLOV.

I've tried some, and the one I prefer is this one.


But feel free to make your own choice - even Notepad (or

Comments Welcome to Renato.Protti@Skynet.be

Edlin :-))

2/10

Writing xml Gauges for FS-2004


Renato C. PROTTI

How to setup the gauge file


Let's now have a look inside the gauge file :
<?xml version="1.0"?>
<!DOCTYPE Gauge SYSTEM "@gauges.dtd">
<Gauge Name="AP" Version="1.0">
<!-- Version 1.01 (YYMMDD) by RcP for ... -->
<Image Name="gauge_bckgrnd.bmp" ImageSizes="469,70,469,70" />
... ... ...
</Gauge>
I use to start with : <?xml version="1.0"?>, but it's not my choice, it has to be so ...
This is to tell the parsers (programs dealing with xml) that it's an xml type 1.0
for I'll use a kind of Parser to verify my source (defined at next line).
Next is : <!DOCTYPE Gauge SYSTEM "@gauges.dtd">, this is a personnal choice to use a controller to check
my source.
@gauges.dtd in a rename of gauges.dtd file from the Panel_SDK\Inc
This statement signals to my Editor that the checks are to be performed against the Document Type
Definition (DTD) file. (To ensure the file is "Well Formed")
This file becomes mandatory in the directory where the source file is located .
If it misses, no editing nor parsing are then possible and an error is generated.
The contents of a DTD file is used as syntax template to check consistency of data described by xml file.
This DTD is related to the needs of parser inputs to avoid that bad definitions to produce garbage at the
time data is used.
DTD is also used to generate default values if you omit them in the source.
Another method was suggested ...
Looking into Panel_SDK\Inc directory, we can see another file Gauges.xdr
XDR Xml Data Reduced was an early days proposal, but seems never been converted into recommandation
by W3C.
So we'll forget that file and use the gauges.dtd instead.
Then on next line: <Gauge Name="AP" Version="1.0">,
This is the real beginning of the xml source file.
<Gauge Name="Name of the gauge" Version="1.0">
The version="1.0" here is not the gauge version, but the gauge-file-source version.
As we'll see later, the tag <Gauge> is the beginning of gauge source
It has 2 attributes : Name and Version.
Remember the attributes are data description of parameters to be passed through the specific FS-Parser to
the specialized routines to handle gauge objects.
Here, we just tell the Parser : We have defined the attributes according to rules of Version=1.0
Let's say it with other words : This source is for Parser Version 1.0.
From here to the final tag : </Gauge> we'll find the Gauge Objects data definitions and attributes.
Next line is :<!-- Version 1.01 (YYMMDD) by RcP for ... --> , this line (or paragraph) starting with <!-- and
ending with --> is Comments.
You put there anything you desire (Version 1.01 here is really the source version #) and this information is
just a comment.
Next : <Image Name="gauge_bckgrnd.bmp" ImageSizes="469,70,469,70" />,
The first object of a gauge is the Background image (if one exists).
This image must reside in the same directory of the source or in the 1024 or 640 subdirectories.
Note the ending "/" - this is equivalent to </Image> as end-tag

Comments Welcome to Renato.Protti@Skynet.be

3/10

Writing xml Gauges for FS-2004


Renato C. PROTTI

Remember that in xml all data description begins with a beginning tag and an ends with an ending tag (End
tag = "/" followed by Beg-tag) ei. <Hello> ..... </Hello>

DTD - Document Type Definition file Quick tour ...


Let's have a look inside the dtd file and try to understand what it means.
Remember :
The contents of a DTD file is used as syntax template to check consistency of data described by xml file.
DTD is also used to generate default values if you omit them in the source.
Example from gauges.dtd file :
...
<!ELEMENT
<!ATTLIST

Image
Image
Name
ImageSizes
DegreesPointsTo
Bright
PointsTo
UseTransparency
Luminous
Alpha
NoBilinear

(Axis? , Nonlinearity?) >


CDATA #REQUIRED
CDATA #IMPLIED
CDATA '0'
(Yes | No ) 'No'
(East | South | West | North ) 'East'
(Yes | No ) 'Yes'
(Yes | No ) 'No'
(Yes | No ) 'No'
(Yes | No ) 'No' >

Note that in gauges.dtd all definitions are also made using xml.
In this short extract of the file we find the description if Image definition.
Every definition is two parts : !ELEMENT and !ATTLIST
Image
(Axis? , Nonlinearity?) >
1. <!ELEMENT
After <!ELEMENT we find the Element.Type (Image) and the description of sub elements needed or allowed
after <Image> and before </Image> tag
Axis and Nonlinearity will be described somewhere in the dtd file with their AttributesList

2.

<!ATTLIST

Image
Name
ImageSizes
...

CDATA #REQUIRED
CDATA #IMPLIED

<!ATTLIST describes all the attributes required or allowed for this Element.Type (Image)
Name
CDATA #REQUIRED means : there must be an attribute Name as String
ImageSizes CDATA #IMPLIED means : there should be an attribute ImageSizes as String, and if not
present, the real image sizes will be used.
In the gauge source, there was :
<Image Name="gauge_bckgrnd.bmp" ImageSizes="469,70,469,70" />,
The definition is "Well Formed" as it contains Name and ImageSizes before the ending "/>" and that the
values of both attributes are Strings between quotes.
3.
PointsTo
(East | South | West | North ) 'East'
Other Attributes descriptions are optional attributes : PointsTo is an enumeration list with allowed values :
(East or South or West or North) if omitted East is assumed.
Look what our statement becomes once parsed :
<Image Name="gauge_bckgrnd.bmp" ImageSizes="469,70,469,70" />,
is converted in :
<Image Name="gauge_background.bmp" ImageSizes="469,70,469,70" DegreesPointsTo="0" Bright="No" PointsTo="East"
UseTransparency="Yes" Luminous="No" Alpha="No" NoBilinear="No" />

Comments Welcome to Renato.Protti@Skynet.be

4/10

Writing xml Gauges for FS-2004


Renato C. PROTTI

The (LIFO) Stack -

The last to enter is the first to exit.

Beside the functions of dealing with objects to display, reading Aircraft or Environment Variables, setting
values thru Events, FS-9 internal Parser can call some functions to Calculate, Test and Branch.
It is also able to manage some local variables that can be used in other functions.
Let's have a look on Calculations and Tests philosophy.
The calculations an tests functions use a storage technique called Stack.
Stack technique is often used when data storage cannot be defined at coding time, but is to be allowed on
demand according to the amount of data to be processed.
Some systems or machines are "stack oriented", meaning that they have special sublogic or hardware to deal
very quickly with a stack structure.
But what is it ?
Imagine the MainMemory as a spreadsheet where there is only one column.
Imagine the column goes from row1 to row4096.
While coding your program, you put the constants and defined variables from row1 down to row96.
But you know you'll need to get external values to be handled sometimes two at a time and sometime five at
a time and even different next time.
Imagine for some reasons that you'll call asynchronous processes that will also need room for a given time
and free it when they exit processing.
Once again, stack is a good solution to temporarily allow and free memory
If you determine that beginning from row4096 and growing toward row3000 (with decrementing addresses),
you can call that portion of memory "The Stack".
If you fill the space with values beginning at row4096 then row4095 then row4094 you just need to know
where you are (TopStackPointer)
In our example, row3000 will be TopStackLimit not to be exceeded.
If you don't need anymore one value (the last) you just increment your TopStackPointer and you forget the
value.
When a new process starts, it just need to know where the TSP is pointing, store that value and use as many
cells it needs for its job, then before leaving it restores the previous value in the TSP and things are reset as
before.
With this technique, you can call a sub-process as many times as wanted and everyone uses its amount of
needed storage, then restores it at the end for next candidate.
The constraint of this LIFO Stack technique is that only the TopOfStack cell is known, one must know what is
usable under it.
So it's nothing else : The last value is used first - The name : "Last In First Out"
Two operations :
Push : Decrement TSP and Place data into current location.
Pop : Get the data from current location and increment TSP
Example of use :
Suppose we need to add two values ... A = B + C
We "push the first (B) onto top of the stack" (update TSP).
We "push the second (C) onto top of the stack" (Update TSP).
We call the "additionner" that "pops both values from top of the stack" (updates TSP).
It makes calculation and "push the result onto top of the stack" Update TSP) and exits.
We now "pop the result (A) from top of the stack" (Update TSP) and we have the result.

The Post fixed notation mechanism (Reverse Polish Notation)


From the preceding example, we observed the stack behaviour when data is passed to and from a sublogic.

Comments Welcome to Renato.Protti@Skynet.be

5/10

Writing xml Gauges for FS-2004


Renato C. PROTTI

Now, suppose that instead of calling the "additionner", wa call a "calculator", we need to tell not only the
values, but also the operation to perform.
To perform A = B + C, the operation will be :
TSP next = 4020
Push B onto Stack updating TSP next = 4019
Push C onto Stack updating TSP next = 4018
Push "PLUS" onto Stack updating TSP next = 4017
Call "Calculator" (it will pop 4018, seeing that its a plus, it knows it needs two more values, it pops 4019 and
4020 updating TSP next = 4020
After computing its addition, it pushes A onto the stack updating next = 4019.
On return we pop A from 4019 and update to next = 4020 as before.
To calculate the sum : A = B + C was executed like :
Push B : Push C : Push "+" : Pop A (assuming that Push and Pop update the TSP).
Imagine now that we will use a Macro to facilitate pushes and pops.
It should be some expression like :
Macro:
CALC(BC+=A)
Generating :
Push B
Push C
Push +
Call CALC
Pop A
Suppose now that the calculation is a little more complex A = (B + C) * D
our expression would become :
CALC(BC+D*=A)
Generating
Push B
Push C
Push +
Call CALC
Push D
Push *
Call CALC
Pop A
As we can see :
Operands are always placed before operators.
Parentheses are no more needed
Order of operations is critic (but this was true in normal notation too)
This way of proceeding with calculation is called the postfixed notation better known as Inverted Polish
Notation. (Polish because the first description of the method was made by : Jan Lukasiewicz, a Polish
mathematician 1878-1956)
This method is widely used (internally) in the stack oriented computers.
Some words about Testing using the Stack.
Testing is exactly the same kind of job as adding :
b+c is done by push b, push c, push + call CALC
and a<b is done by in push a, push b, push < call CALC
The only difference is that when + is evaluated, the result is a+b and when < is evaluated, the result is 0 or
1 (False or True) according to a and b
Our internal FS-9 Calculator and Expression Evaluator needs to find its arguments on the stack and organized
(you guessed it !) according to IPN rules.

Comments Welcome to Renato.Protti@Skynet.be

6/10

Writing xml Gauges for FS-2004


Renato C. PROTTI

In the same way, results are always found on top of stack on exit.
With these informations we can now proceed to our detailed investigation into gauges.xml syntax and
principles of operation.

Back to the Source - The playground ...


As told before, FS-9 xml-Parser is able to deal with variables, events etc ...
Ex. of Variables :
(A:AUTOPILOT MASTER,bool)

Variable Type A = From Aircraft - !Use units!


see Parameters.doc in the SDK (Aircraft Data ...)
see Units.doc for a full description of units
Read Only variable - to modify use (K:Events)

(P:Local time, seconds)

Variable Type P = From Program - !Use units!


see Parameters.doc in the SDK (Simulator Global Data)
Warning - this is often described as a E:Var. (wrong)

(E:AMBIENT WIND
DIRECTION, Degrees)

Variable Type E = From Environment - !Use units!


see Parameters.doc in the SDK (Environment Data)
Warning - this is a mistake - it's a A: type variable

(G:Var1) ... (G:Var9) ...

Variable Type G = From Gauge - !No Unit after!


belongs to the actual gauge
Read / Write variables

(L:My Variable,bool)

Variable Type L = From Local - !Use units!


Belongs to the full panel (shared between gauges)
Read / Write user variables

(>K:GEAR_DOWN)

Variable Type K = From Key Event - !Use units!


Used to alter an (A:Variable,Unit)
see EventID.doc in the SDK for details
Note the > before K this is a Write only event
Note some Events (-SET) need value on TopOfStack

(M:X)

Variable Type M = From Mouse - !Use no units!


X and Y are the coordinates of the mouse relative to point
0,0 of the gauge.
Read Only variable

Beside Variables, FS-9 xml-Parser offers 50 internal registers related to stack operations.
Stack Operations and Registers :
c - Clear the stack
d - Duplicate TopOfStack value
r - Reverse TopOfStack values
s# - Store TOS value to register
l# - Load Register to TOS value
sp#- Store and Pop TOS to Reg

Stack pointer is reset back to initial value.


and obviously update TopofStackPointer (Push)
exchange last and previous Stack values
Copy TopOfStack values to an Internal Register
No update of TopofStackPointer (this is not a pop)
Load TopOfStack values from an Internal Register
and obviously update TopofStackPointer (Push)
Pop TopOfStack values to an Internal Register
and obviously update TopofStackPointer (Pop)

Please see the Panel SDK


For all information regarding string, numerical and Logical operators

Comments Welcome to Renato.Protti@Skynet.be

7/10

Writing xml Gauges for FS-2004


Renato C. PROTTI

Special Considerations about Operations


We'll focus on some particular cases of Test and Selection.

? - Select characters values in a string

Rule : The third operand defines which of first or Second operand will take place as result of the
operation
Color="%('GREEN' 'YELLOW' (A:Value, percent) 49.99 < ?)"

The third operand "(A:Value,percent) < 51.59" defines which of first "GREEN" or Second "YELLOW"
operand will take place as result of the operation.
If (A:Value,percent) < 51.59 then "GREEN" else "YELLOW"
Push "GREEN"
Push "YELLOW"
Push (A:Value,percent)
Push 49.99
Push <
Evaluate (Pop operator and operands - replace by result (0 or 1) then Pop operand #=result. (on Top of
Stack we find "GREEN" if 1 operand is popped and "YELLOW" if 0 operand is popped.
So "GREEN" if (A:Value,percent) < 49.99
or "YELLOW" if (A:Value,percent) not < 49.99
Just a little more complex with two ? tests :
Color="%('GREEN' 'YELLOW' (A:Value, percent) sp1 l1 49.99 < ? 'RED' l1 79.99 < ?)"

Push "GREEN"
Push "YELLOW"
Push (A:Value,percent)
Store and pop (A:Value,percent) to Reg-1
Push Reg-1
Push 49.99
Push <
Evaluate --> GREEN if (A:Value,percent) < 49.99
or "YELLOW" if (A:Value,percent) not < 49.99
Push "RED"
Push Reg-1
Push 79.99
Push <
Evaluate --> "PREVIOUS COLOR" if (A:Value,percent) < 79.99
or "RED if (A:Value,percent) not < 79.99
To summerize :
GREEN if (A:Value,percent) < 49.99
YELLOW if 49.99 < (A:Value,percent) < 79.99
RED if 79.99 < (A:Value,percent)
In this example, GREEN, YELLOW or RED is the value of Attribute : Color

%{Case} - Select characters values in a string according to Value of choice

Rule : Every character between <String> ... and ... </String> is part of the string.
We'll have to choose a message to be displayes according to the value of (L:MSG,enum)
<String>
%((L:MSG, enum))%{case} %{:00} %{:01}PARKING BRAKE ON%{:02}IGNITION A ON %{:03}IGNITION B ON
%{:04}L REV ARMED %{:05}R REV ARMED %{:06}L PACK OFF %{:07}R PACK OFF %{:08}NO SMOKING %{:09}SEAT
BELTS %{:10}LANDING LIGHTS %{:11}TAXI LIGHTS %{:12}L 14TH SOV CLSD %{:13}14TH ISOL OPEN %{:14}R 14TH
SOV CLSD %{:15}L 10TH SOV CLSD %{:16}APU LCV OPEN %{:17}10TH ISOL OPEN %{:18}R 10TH SOV CLSD %{end}
</String>

Make the effort to understand and then use it like it is.

Comments Welcome to Renato.Protti@Skynet.be

8/10

Writing xml Gauges for FS-2004


Renato C. PROTTI

<Case value> - Pay attention to not confuse with the previous

Rule : This xml-Tag describe how ths sublogic will deal with the selected value
<Case Value="1"> ... Execute until </Case> if value of selected variable is = "1"
Example:
<Element>
<Position X="47" Y="278" />
<Select>
<Value>(A:Value, enum) </Value>
<Case Value="0">
<Image Name="Image0.bmp" Bright="Yes" ImageSizes="25,5,25,5" />
</Image>
</Case>
<Case Value="1">
<Image Name="Image1.bmp" Bright="Yes" ImageSizes="25,5,25,5" />
</Image>
</Case>
<Case Value="2">
<Image Name="Image2.bmp" Bright="Yes" ImageSizes="25,5,25,5" />
</Image>
</Case>
</Select>
</Element>

According to the value of (A:Value,enum) we choose Image0, Image1 or Image2


Another example for the fun, a combined one :
<Element>
<Position X="160" Y="210" />
<Select>
<Value>0 1 (A:GEAR LEFT POSITION,percent) s0 1 < ? 2 l0 99.99 < ? 0 1 (A:GEAR CENTER POSITION,percent) s0 1 < ? 2 l0
99.99 < ? + 0 1 (A:GEAR RIGHT POSITION,percent) s0 1 < ? 2 l0 99.99 < ? +</Value>
<Case Value="0">
<Image Name="gear_up.bmp" Bright="Yes" ImageSizes="73,24,73,24" DegreesPointsTo="0" PointsTo="East"
UseTransparency="Yes" Luminous="No" Alpha="No" NoBilinear="No" />
</Case>
<Case Value="2">
<Image Name="gear_3red.bmp" Bright="Yes" ImageSizes="73,24,73,24" DegreesPointsTo="0" PointsTo="East"
UseTransparency="Yes" Luminous="No" Alpha="No" NoBilinear="No" />
</Case>
<Case Value="3">
<Image Name="gear_3red.bmp" Bright="Yes" ImageSizes="73,24,73,24" DegreesPointsTo="0" PointsTo="East"
UseTransparency="Yes" Luminous="No" Alpha="No" NoBilinear="No" />
</Case>
<Case Value="4">
<Image Name="gear_3red.bmp" Bright="Yes" ImageSizes="73,24,73,24" DegreesPointsTo="0" PointsTo="East"
UseTransparency="Yes" Luminous="No" Alpha="No" NoBilinear="No" />
</Case>
<Case Value="6">
<Image Name="gear_3green.bmp" Bright="Yes" ImageSizes="73,24,73,24" DegreesPointsTo="0" PointsTo="East"
UseTransparency="Yes" Luminous="No" Alpha="No" NoBilinear="No" />
</Case>
</Select>
</Element>

In the Value paragraph we evaluate an expression combining test-addition


We have 3 variables and we want to add 0 if value-1 is < 1 or 2 if it is > 99.99
In other words, we want 0 if value is 0, 1 if it is between 1 and 99 and 2 if it is = 100
one value for each variable (Left, Center, Right)
then we want to add all 3 in order to obtain :
0 if all gear up
6 if all gear down
and intermediate if gears in movement.
Then with this value we'll choose the Image to be displayed:
0 : Gear Up
3 : Gear moving (3Red)
6 : Gear Down (3Green)
1 : Nothing
4 : Gear moving (3Red)
2 : Gear moving (3Red)
5 : Nothing

Comments Welcome to Renato.Protti@Skynet.be

9/10

Writing xml Gauges for FS-2004


Renato C. PROTTI

If ... Then ...Else - All comes back to that !


Everyone having already programmed at least 3 lines of code has met the
If ... Then ... Else.
This form is used into the <Value>...</Value> and
Mouse actions <Click Kind="LeftSingle" (A:...,...) If{...}els{... }</Click>
Here again, the stack is used for the evaluation and decision making.
Rule : If{... do if true ...} els{... do if false ...}
Example :
(L:DUMMY03, Bool) if{ 6 (>L:MSG,enum) } els{ 0 (>L:MSG,enum) }

Push L:DUMMY03,bool
Evaluate
True : Push 6 then pop to (>L:MSG) (note the > to signify write to ...)
False : Push 0 then pop to (>L:MSG)
Translated : If L:DUMMY03 then L:MAG = 6 else L:MSG = 0

GOTO - As you want !


Another well known statement (even if often avoided) can be used to jump to the corresponding label.
Rule :
g0 - jumps to :0
g1 - jumps to :1
...
g## - jumps to :##

More to come (050716)

Comments Welcome to Renato.Protti@Skynet.be

10/10

S-ar putea să vă placă și