Board index FlightGear Development Nasal

Spoken GCA

Nasal is the scripting language of FlightGear.

Re: Spoken GCA

Postby Hooray » Thu Oct 12, 2017 4:10 pm

For the tabbed pilot/controller and developer interface, it should be sufficient to look at $FG_ROOT/Nasal/canvas/gui/dialogs/AircraftCenter.nas, which is our primary example of a tabbed interface.

It's using 3 different buttons to emulate tabs, and uses a ScrollArea for all entries (which may be another idea for the GCA UI).

https://sourceforge.net/p/flightgear/fg ... Center.nas
Image

For the ScrollArea, there is existing example code to be found here: http://wiki.flightgear.org/Canvas_Snipp ... ScrollArea

The callsign-based position-tree lookup should be easy to implement by traversing all ai/mp children and searching for a matching callsign - for example using something like this

Code: Select all
var validateCallsign = func(callsign) {

var RESULT = {NOT_FOUND:0, FOUND:1};
var list = props.globals.getNode("/ai/models").getChildren("aircraft");

foreach(var l; list) {
if (l.getNode('callsign').getValue() == callsign) return [RESULT.FOUND, l.getNode('position')];
}
return [RESULT.NOT_FOUND, nil];
} # validateCallsign;

var getPositionNodeByCallsign = func(callsign) {
var (result, positionNode) = validateCallsign(callsign);
debug.dump(positionNode);
} # getPositionNodeByCallsign


getPositionNodeByCallsign('some-callsign');

Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11353
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Thu Oct 12, 2017 4:27 pm

There is a complication that is bothering me: when the TTS "steps on the tail".

I have tried to keep the phraseology as brief and concise as possible, according to the regulations provided by Alant. Despite that, often on short final the Controller must give urgent instructions ... and the TTS is still busy.
So the instruction is delayed , hence the pilot reacts late and a new urgent instruction must be given ... In short, an avalanche that only makes the situation worse.

ITRW controller should skip "standard" instructions in order to priority "urgent" ones.
Is there any "flag" to check whether TTS is busy or not?
Is it possible to clean/erase the TTS queue when necessary ?
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Thu Oct 12, 2017 4:43 pm

Sorry, I don't know - I would need to look at the source code, Torsten would be the best person to tell you whether this is possible/supported or if it can be added, best to ask via the devel list - this is about the Flite TTS engine and its integration in FlightGear.
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11353
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Thu Oct 19, 2017 4:33 pm

I am modifying the configureGCA() func in order to emulate a "tabbed" dialog, and need help about managing layers/items.
Image We have a top layer (tabLayer) containing the 3 "tab" buttons.
Then a center layer (fieldsLayer) containing the relevant widgets (labels & LineEdit) and a bottom one (buttonLayer) with the "Apply" and "Start/Stop" buttons.
The screenshot was obtained calling:
Code: Select all
configureGCA(gcaObject, nil, defTab=2) ;
Each tab has an associated "mask" set. At startup, all widgets are created and registered into a fields vector.
Code: Select all
fields = [ {label:<label_widget>, field:<lineEdit_widget>, text:<inputs[i].text> }, {...}, ... ]

Only those not-masked widgets are added to the fieldsLayer:
Code: Select all
   mask = masks[tab];
   foreach(hash;fields) {
     if(!contains(mask, hash.text)){
       printf("addItem(%s)",hash.text);
      fieldsLayer.addItem(hash.label);
      fieldsLayer.addItem(hash.field);
      }
      }
Up to here all seems OK.
But when clicking a "tab" button ... :shock:
The idea is (was?) to (1) remove all items from the fieldsLayer, (2) add the not-masked ones, and (3) update the currentTab value.

I have tried several ways to achieve that. All without success.
Code: Select all
var removeFields = func() {
   if(mask==nil) return;
   mask = masks[currentTab];
   foreach(hash;fields){
     if(!contains(mask, hash.text)){
       printf("removeItem(%s)",hash.text);
    fieldsLayer.removeItem(hash.label);
    fieldsLayer.removeItem(hash.field);
    }
   }
} # removeFields

var placeFields = func(tab) {
   #~ fieldsLayer.clear(); # ??
   #~ fieldsLayer.removeAllItems(); # ??
   removeFields();

   mask = masks[tab];
printf("81) tab=%i, %i fields.",tab,size(fields)-size(mask));
debug.dump(mask);

   foreach(hash;fields) {
     if(!contains(mask, hash.text)){
       printf("addItem(%s)",hash.text);
      fieldsLayer.addItem(hash.label);
      fieldsLayer.addItem(hash.field);
      }
      }
   currentTab = tab;
} # placeFields

The placeFields() func is launched on clicking a "tab" button. The printf and dump lines show me that the flow of the code actually passes through there.

For your reference, I've pasted the whole script on the wiki page.
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Fri Oct 20, 2017 5:03 pm

Hi, just briefly (may respond in more detail later on): Honestly, it seems like you are overcomplicating matters here. What you are trying to do should be much simpler than the code/approach you have posted. Like I said previously, I'd suggest to check out AircraftCenter.nas in $FG_ROOT/Nasal/canvas/gui/dialogs and then use that as an example on assigning/changing a layout by setting a button. For instance, there is a wiki page detailing the Canvas Layout APIs available, too:

https://sourceforge.net/p/flightgear/fg ... Center.nas
http://wiki.flightgear.org/Canvas_Layout_System
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11353
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Fri Oct 20, 2017 5:42 pm

I'm confused:
Almost all (canvas-layers relevant) structure in my script was indeed taken from the AircraftCenter.nas.
I only skipped the "stretch" and "ScrollArea" stuff. (I still don't quite understand how they work)

I really would appreciate it if you would lend a hand in this.
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Fri Oct 20, 2017 5:53 pm

Sure, if I am not mistaken, AircraftCenter.nas is using a single VBoxLayout that is cleared whenever the tab's button is clicked - referring to the toggle callback registered in the addPage() method - apart from that it's really just marking the button itself as having been clicked:

Code: Select all
addPage: func(name, filter)
  {
    var b = gui.widgets.Button.new(me._root, style, {})
                              .setText(name)
                              .setCheckable(1);
    me._tab_bar.insertItem(me._tab_bar.count() - 1, b);

    b.listen("toggled", func(e)
    {
      if( !e.detail.checked )
        return;

      if( me._active_button != nil )
        me._active_button.setChecked(0);
      me._active_button = b;

      me._list.clear();
      me._show_more = nil;

      settimer(func me.fillList(pkg.root.search(filter)), 0, 1);
    });

    if( me._active_button == nil )
      b.setChecked(1);

    return me;
  },


Subsequently, a timer is registered to populate the VBoxLayout with entries, invoking the fillList method, because that one runs asynchronously using a worker thread (not needed for our use-case), which ends up at _addPackageEntries to populate the layout with new widgets.

Thus, I guess, it would be much better to disregard the gca use-case for now, and simply test this approach using a self-contained Nasal script that can be executed via the Nasal Console
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11353
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Fri Oct 20, 2017 7:00 pm

I think that I did it in a similar way
Code: Select all
var pilot_btn = createButton(root,"Pilot",[75,25],func { placeFields(0,fields);});
Clicking the button triggers the placeFields() func:
Code: Select all
var placeFields = func(tab,fields) {
   fieldsLayer.clear();
   mask = masks[tab];
printf("81) tab=%i, %i fields.",tab,size(fields)-size(mask));
   foreach(hash;fields) {
     if(!contains(mask, hash.text)){
       printf("addItem(%s)",hash.text);
      fieldsLayer.addItem(hash.label);
      fieldsLayer.addItem(hash.field);
      }
      }
} # placeFields

So the layer is cleared and the proper widgets would be added ... but they don't.
Please see the last mssg I've sent to the devel-list (Subject: Canvas layer.addItem() / removeItem() )
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Tue Oct 31, 2017 4:23 pm

Hi, I just noticed that you updated the wiki and that you seem to be working on callsign-based lookup of AI/MP properties roots - given the code you have now, that should be pretty straightforward to implement by changing the validation routine, to check first if the entered value is a valid callsign, by traversing all AI/MP aircraft and their associated callsign in a foreach loop - if the callsign matches, you can return STATUS.SUCCESS.
The only thing needed then is to store the property as part of the GCA object - so that it does no longer matter what the actual input is (two modes then: an actual property or a callsign representing a property path).

For example, using the following untested pseudo code, based on: http://wiki.flightgear.org/Howto:Workin ... properties
Code: Select all
'AircraftRoot': func(input) {

# check first if the input is a valid callsign:
var list = props.globals.getNode("/ai/models").getChildren("aircraft");
var total = size(list);

for(var i = 0; index < total; i += 1) {
    var callsign = list[i].getNode("callsign").getValue();
    print(callsign);
   if (callsign == input) return STATUS.SUCCESS; # this is where we'd want to store the property separately, for later use
}


var root = props.getNode(input);
if (root == nil) return STATUS.FAILURE; # error

var required_props = ['altitude-ft', 'longitude-deg', 'latitude-deg'];

foreach(var rp; required_props) {
 if (getprop(input ~"/" ~rp) == nil) return STATUS.FAILURE;
} # foreach

return STATUS.SUCCESS; # valid root node
},
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11353
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Tue Oct 31, 2017 6:31 pm

Hi Hooray,

If I understood you correctly, I think that your proposal would make sense if the UI dialog had a "callsign" entry.
Currently it does not have it, then the callsign is taken from the root/callsign node (if it exist) or from the /sim/multiplay/callsign one (if not).
Are you suggesting to include an specific "Callsign" entry ?
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Tue Oct 31, 2017 6:40 pm

I guess, I'd simply abuse the first input field for that, and then re-label that accordingly (property branch or AI/MP callsign) - in fact, it could even use JUST callsigns (for the main aircraft, too) - using the same heuristics. The only thing needed is determining whether the entered string is a callsign in use by any AI/MP aircraft or if it matches the callsign of the main aircraft (also stored in a property)
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11353
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Tue Oct 31, 2017 7:16 pm

Oh, now I understand the idea. :)
To relabel the entry to "Callsign" and accept a root node (as "/position") or a simple string (the straightforward callsign).
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Tue Oct 31, 2017 7:22 pm

yeah, I guess that this should be easier/more intuitive to use from a UI standpoint - as in checking the input string against the local callsign, and return STATUS.SUCCESS if it matches that, or check the input against the vector of AI/MP callsigns.

Obviously that would need a way to store a callsign-specific property - e.g. by setting the property transparently or adding a callsign2propertyPath() helper function to do so ?

The "root node" could be left undocumented then, i.e. only left for power-users - most people will use the callsign anyway, right ?

Consider the following pseudo code for the Nasal console - checking if "rleibner" is a valid/known callsign:
Code: Select all
var STATUS = {SUCCESS:0, FAILURE:1};

var getActiveAIList = func(type='aircraft') {
 var list = props.globals.getNode("/ai/models").getChildren( type );
 return list;
}


var isValidCallsign = func(text) {
if (text == getprop("/sim/multiplay/callsign")) return [STATUS.SUCCESS, props.getNode("/position") ] ;

var AIList = getActiveAIList();
foreach(var listItem; AIList) {
var currentCallsign = listItem.getNode('callsign',1).getValue();
print("Checking AI/MP callsign: ", currentCallsign);
if (currentCallsign == text) return [STATUS.SUCCESS, listItem.getNode('position') ];
}

return [STATUS.FAILURE, nil];
}


debug.dump( isValidCallsign("rleibner") );
#####
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11353
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Tue Oct 31, 2017 10:15 pm

Hooray wrote in Tue Oct 31, 2017 7:22 pm:Consider the following pseudo code for the Nasal console
I like it ! I buy it.

Only I donno why you wrote
Code: Select all
listItem.getNode('callsign',1).getValue()
and not simply
Code: Select all
listItem.getNode('callsign').getValue();
I think that there is only a callsign prop under each /ai/model/aircraft[n]/ :?:
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Wed Nov 01, 2017 7:35 pm

the final argument (1) is there to ensure that the node is created if it isn't there - which ensures that we get a valid props.Node object in return - otherwise, the whole thing may trigger a runtime error, because the callsign property isn't there - which in turn means that we cannot invoke any methods on it. Thus, when using nested/chained method calls like that, it is usually a good idea to make sure that the property is indeed created, so that the whole thing cannot break.

The background being that some AI/MP nodes in the tree may not yet be "complete", i.e. not yet have all properties set/updated - so that incomplete state may be in the tree.
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11353
Joined: Tue Mar 25, 2008 8:40 am

PreviousNext

Return to Nasal

Who is online

Users browsing this forum: No registered users and 1 guest