FlightGear/Gli script XML
In questo capitolo si descrive come costruire l'impalcatura principale di FlightGear, impalcatura fatta di codice XML che si appoggia sulle fondamenta di programmi scritti in C++. L'utilizzo del cosce XML ha permesso, all'inizio, di definire la possibilità di programmare facilmente l'interfaccia grafica GUI (Graphical user interface) senza dover scrivere righe in codice C++. Questo approccio, molto simile a quello utilizzato per costruire le interfacce grafiche Web, ha permesso di allargare notevolmente la fascia dei possibili programmatori di FlightGear prima spaventati dalla complessità del codice C++.
Successivamente l'uso dell'XML si è allargato ad altre parti del programma ed è diventato lo strumento di interfaccia tra il nucleo di FlightGear in C++ ed il linguaggio di programmazione Nasal utilizzato per scrivere efficaci script che di fatto coprono la maggior parte del codice scritto.
Binding
[modifica | modifica sorgente]Nei linguaggi di programmazione il concetto di Binding è quello di generare un legame tra due differenti insiemi di codice. Ad esempio il nucleo portante di codice di FlightGear è formato da una serie di programmi in C++ che causa la loro intrinseca complessità sono gestiti solo da pochi ed esperti programmatori. Il Bindig fa si che i dati provenienti da alcune proprietà, dette per l'appunto <PropertyList> possano essere modificate da appositi comandi XML i quali non solo altro che chiamate al codice C++ che costituiscono il nucleo del programma.
Un binding si attiva quando capita un evento, ad esempio in FlightGear quando si clicca su un oggetto presente nello scenario o nell'aereo, quando si opera con la tastiera o con il joystick, quando si clicca su una voce di menu o si clicca su bottoni e/o si inserisce valori nelle aree di testo. In questo caso si ha un binding tra le azioni di input intercettate dal sistema operativo, ritrasmesse a dei processi di ascolto presenti nel codice in C++ di FlightGear e quindi analizzate dal processo descritto all'interno del binding.
Ad esempio, un caso molto semplice è il binding che gestisca la chiusura di una finestra:
<binding>
<command>dialog-close</command>
</binding>
Il comando dialog-close è un comando di sistema per la chiusura di una finestra, diversamente è possibile aprire una qualsiasi finestra di dialog descritta come codice xml nella directory $FG ROOT/fgdata/gui/Dialogs.
<binding>
<command>dialog-show</command>
<dialog-name>location-in-air</dialog-name>
</binding>
In questo caso si mostra la finestra di dialog il cui codice xml è definito nel file location-in-air.xml.
Esempio di binding per il joystick
[modifica | modifica sorgente]Un esempio più complesso è la modifica dei valori rilasciati da un asse del joystick in modo che siano compatibili con i valori previsti da FlightGear. Ovviamente alcune cose, come l'interfacciamento tra il PC ed il joystick, avviene tramite programmi di sistema operativo e quindi per mezzo di chiamate scritte in C++. Queste chiamate di basso livello permettono la generazione di un albero delle proprietà del dispositivo joystick raffigurato in questo esempio per il solo asse 0 e bottone 0 da una struttura XML del seguente tipo:
<PropertyList>
<name>Logitech Inc. WingMan Force 3D</name>
<axis n="0">
<desc>Aileron</desc>
</axis>
<button n="0">
<desc>Brakes</desc>
</button>
</PropertyList>
Questa struttura contiene tre oggetti, il nome della proprietà (<name>..</name>) e due variabili <axis n="0">..</axis> e <button n="0">..</button>. A questo punto è possibile fare il binding ovvero legare il valore presente in una certa proprietà con qualche trasformazione che può avvenire richiamando un codice in C++ e/o utilizzando uno script in Nasal.
<PropertyList>
...
<axis n="0">
<desc>Aileron</desc>
<binding>
<command>property-scale</command>
<property>/controls/flight/aileron</property>
<factor type="double">1.9</factor>
<offset type="double">-0.055</offset>
<power type="double">1.0</power>
</binding>
</axis>
In questi caso all'interno di <binding>..</binding> vi è il codice che potrà modificare il valore dell'asse 0 (<axis n="0">) tramite il comando di sistema <command>property-scale</command> che contiene alcuni parametri, il primo è il nome della proprietà dell'albero globale delle proprietà (<property>/controls/flight/aileron</property>) che dovrà essere unito in binding con il valore dell'asse 0 (<axis n="0">). I successivi quattro parametri sono richiesti, tutti o solo in parte, dipende la contesto, dal comando property-scale e definiscono il tipo di trasformazione del dato numerico che realizza il comando property-scale.
È anche possibile fare un binding non verso una chiamata C++ come property-scale ma verso uno script in Nasal, in questo caso potremo avere il seguente codice XML:
<PropertyList>
...
<axis n="0">
<desc>Aileron</desc>
<binding>
<command>nasal</command>
<script>controls.throttleAxis()</script>
</binding>
</axis>
Il questo caso il comando C++ richiamerà l'interprete Nasal (<command>nasal</command>) seguito come parametro dal codice Nasal (<script>...</script>) che nel nostro esempio è semplicemente una chiamata ad una funzione Nasal di nome controls.throttleAxis() posizionata all'interno dell'area che raccoglie tutti gli script Nasal standard di FligtGear.
Elenco dei binding
[modifica | modifica sorgente]I binding ammessi dal sistema sono riportati integralmente in questo link ed, in modo sintetico, ma più completo, in questo link, ma comunque conviene soffermarci su alcuni che sono particolarmente importanti nell'uso quotidiano degli script XML.
Nasal
[modifica | modifica sorgente]È il bindig che permette di attivare uno script in linguaggio Nasal, ad esempio il semplice script permette di stampare il valore dell'asse n. 2 del joystick normalizzato tra 0 ed 1:
<axis n="2">
<desc>Throttle</desc>
...
<binding>
<command>nasal</command>
<script>
var th = (cmdarg().getNode("setting").getValue() - 0.5)*5/8+1;
print(th);
controls.throttleAxis();
</script>
</binding>
</axis>
Come si vede viene definita all'interno dello script una variabile th nella quale viene assegnato un valore con il comando Nasal: cmdarg().getNode("setting").getValue() dopo una certa elaborazione numerica. Poi questo valore viene stampato su consolle: print(th);. Finalmente viene invocato un comando Nasal con controls.throttleAxis();, questo comando si trova definito all'interno del file Nasal posto in $FG ROOT/fgdata/Nasal/controls.nas. Niente quindi impedisce che tutto il codice Nasal sia posto direttamente dentro la parte <script>...</script>.
Property-scale
[modifica | modifica sorgente]È un comando utile per la gestione degli assi del joystick. Un esempio per il suo utilizzo è il seguente:
<PropertyList>
...
<axis n="0">
<desc>Aileron</desc>
<binding>
<command>property-scale</command>
<property>/controls/flight/aileron</property>
<factor type="double">1.9</factor>
<offset type="double">-0.055</offset>
<power type="double">2.0</power>
</binding>
</axis>
Il comando property-scale applica una particolare funzione di taratura sul valore dell'asse del joystick, nell'esempio n=0 (alettoni). Il valore numerico prodotto dal joystick viene chiamato in questa funzione property:
- result = ((property + offset) * factor)^power
Si ottiene nel nostro esempio quindi:
- result = ((property -0.055) * 1.9)^2.0
- Nota: Il parametro power è utilizzato per definire il tipo di curva di risposta del joystick ovvero quella che spesso viene chiamata sensibilità del joystick, ad esempio se power = 1.0 la curva di risposta sarà piatta, ovvero il movimento angolare della superficie mobile rispetto allo spostamento del joystick sarà lineare. Se invece il valore del parametro power è maggiore di 1, allora l'effetto del joystick sarà inizialmente più debole e poi aumenterà più velocemente, maggiore è questo parametro, maggiore è la rampa di salita agli estremi. In questo modo è possibile regolare la risposta dei comandi dell'aereo rispetto allo spostamento del joystick evitando, ad esempio, un'eccessiva sensibilità per piccoli spostamenti centrali del joystick.