Board index FlightGear Development Nasal

Write value to specific vector index // Joystick axes

Nasal is the scripting language of FlightGear.

Write value to specific vector index // Joystick axes

Postby Bjoern » Sat Jan 25, 2020 8:24 pm

I'm trying to ouptut listener values to a vector. Listener 1 is to output to vector index zero, listener 2 to index 1, etc.

In Lua, I'd simply declare a table with a length of the total number of listeners I want to monitor and do "myTable[n] = listenervalue".
Last edited by Bjoern on Tue Jan 28, 2020 9:54 pm, edited 2 times in total.
Bjoern
 
Posts: 484
Joined: Fri Jan 06, 2012 11:00 pm
Location: TXL (RIP)
Version: Next
OS: ArchLinux

Re: Write value to specific vector index

Postby Hooray » Sun Jan 26, 2020 11:56 am

Apparently, you are conflating a few different concepts here - speaking in general, what you said will "just work" - i.e. an empty vector is allocated by using square brackets like this:

Code: Select all
var myVector = [];


To access a certain index, use:
Code: Select all
myVector[0] = 100;


To modify the size of the vector, see the setsize() API.

However, a "table" is usually the equivalent of a dictionary/hash - which is a different beast:

Code: Select all
var myHash = {};
myHash["1"] = 100;


That being said, it only makes sense to use vectors, hashes and listeners together once you understand each of these in isolation - I am not sure about the exact use-case you have in mind, but it seems over-engineered, you might be better off telling us what exactly you are trying to do

There is the added complexity that listeners use handles to functions, that receive a props.Node object - in other words, the setlistener() call will return a listener ID, but that's only meaningful to remove a listener, any work related to the listener/property change, must happen inside the function handling that change. Which means, that you need to understand callbacks, too.

To learn more about vectors, hashes and listeners, I'd suggest to use the Nasal Console and a work through a few wiki tutorials:

http://wiki.flightgear.org/Nasal_Console
http://wiki.flightgear.org/Nasal_Variables
http://wiki.flightgear.org/Howto:Start_ ... s_in_Nasal
http://wiki.flightgear.org/Callbacks
http://wiki.flightgear.org/Using_listen ... ener.28.29

Admittedly, this may seem daunting at first - but the point really is, don't try to use all of these things together unless you understand the lower level building blocks first, or it will all look like gibberish to you.

In other words, I'd suggest to approach the whole topic from a different angle:

  • Create a script that puts all numbers from 0 to 10 into a vector
  • Create a script that puts all numbers from 0 to 10 into a hash
  • Create a script that registers a listener to fire when a property is modified
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: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: Write value to specific vector index

Postby Bjoern » Sun Jan 26, 2020 8:48 pm

Lua's table concept: https://www.lua.org/pil/2.5.html

I want to create script that alerts users if the connected input controller exhibits anomalous axis values after startup.
Motivation is this bug here: viewtopic.php?f=17&t=36797
Tracker discussion: https://sourceforge.net/p/flightgear/codetickets/2185/

A source level fix is being worked on, but I thought that a workaround Nasal script would be a nice exercise.

Basically, what I want to do is:
1. Monitor all 16 available joystick axes ("/devices/status/joysticks/joystick/axis[0]" to "...[15]")
2. Count all axes that are deflected to any extreme value (-1 or +1)
3. If there's more than two fully deflected axes, clamp all flight control outputs to zero and pop up a Canvas-based information window commanding the user to move the input controller to wake it up
4. Once movement has been detected, unlock flight control output, kill the pop-up info window and terminate the script

I've tried to read the joystick properties directly and continuousyl from a nested "while" and "for" loop, which sort of works, but basically polls FG to death.
So I tried to set up listeners. From a "for" loop, this is apparently not possible, see the example way down on this page: http://wiki.flightgear.org/Using_listen ... with_Nasal
My latest attempt was a fixed listener for each of the 16 axes that writes its values to a vector. And that's where I got stuck.

So basically, if I can get all 16 axes to write to their respective position in a vector, I could loop through said vector in a "for" loop and count the axes with excessive input from there.
Bjoern
 
Posts: 484
Joined: Fri Jan 06, 2012 11:00 pm
Location: TXL (RIP)
Version: Next
OS: ArchLinux

Re: Write value to specific vector index

Postby Hooray » Sun Jan 26, 2020 9:15 pm

That sounds possible, and your terminology also sounds like you have done coding before ?

Basically, my suggestion would be to create a helper function/class to monitor a single property, and then invoke/instantiate that as often as needed.

For starters, you will want to write a single piece of code to monitor a hard-coded property, than you change the property/index by turning that into a variable.
At that point you can call your "PropertyMonitor" as often as needed.

And you can also set up the whole thing so that you can register different listeners, or simply use a single listener for all nodes.

Don't get stuck focusing too much on a single algorithm/solution or data structure (vector).

My suggestion would be to write empty function stubs that you populate step by step, i.e.:

  • checkAxis()
  • clampOutput()
  • showWarning()

So basically you need to start simple, i.e. by monitoring a single axis/property and then generalizing things step by step to support different properties/nodes

You don't need to use a vector or a hash to make this work - you could also just use a listener or timer to monitor the corresponding properties and register the monitoring function using a for-loop

For the Canvas popup, see "Popups" in the wiki or "Canvas Snippets"

Really, approach things step by step and then integrate/generalize those bits that work properly
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: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: Write value to specific vector index

Postby Bjoern » Sun Jan 26, 2020 11:16 pm

Hooray wrote in Sun Jan 26, 2020 9:15 pm:That sounds possible, and your terminology also sounds like you have done coding before ?


Heh. With having to work with so many different languages, it's more of a matter of "monkey see, monkey do". I live off provided examples and understand pretty much nothing of the concepts behind it. The Lua manual is a an example for a very good reference for learning the language. Most things are explained straight to the point and there's plenty examples with expected outputs.
The Nasal wiki is much harder to work with, for all its text and quotations. Good if you've got a programming background and want to understand what you're working with, bad if you don't.

For starters, you will want to write a single piece of code to monitor a hard-coded property, than you change the property/index by turning that into a variable.


Something like this?

Code: Select all
var axis0val="";
var axis1val="";

var axis0 = setlistener("/devices/status/joysticks/joystick/axis[0]", func(n){ axis0val = n.getValue(); });
var axis1 = setlistener("/devices/status/joysticks/joystick/axis[1]", func(n){ axis1val = n.getValue(); });

var axisvector[axis0val,axis1val];


For the Canvas popup, see "Popups" in the wiki or "Canvas Snippets"


Funnily enough, rendering the box is one thing I got working without much cursing by manipulating the provided example from the Wiki. Don't ask me

Really, approach things step by step and then integrate/generalize those bits that work properly


That's my usual approach. But the issue of not being able to write directly to a vector had me stumped.



- Edit:

Wait a sec...with a timer, I could poll each property directly every n secondsand thus not flood FG to death with requests. I might just try that.


- Edit #2:
Code: Select all
var timer = maketimer(1, func(){
   var deflectedaxes=0;
   for (var i=0; i < 16; i=i+1) {
      #print("Axis "~i~" value: "~getprop("/devices/status/joysticks/joystick/axis["~i~"]"));
      if((math.abs(getprop("/devices/status/joysticks/joystick/axis["~i~"]")) > 0.5) and (math.abs(getprop("/devices/status/joysticks/joystick/axis["~i~"]")) <= 1.0)) {
         print("Axis "~i~" is deflected!");
      #   deflectedaxes = deflectedaxes + 1;
      }
   }
});


timer.start();


Doesn't win any beauty contests, but I get the console/log output I want while not flooding FG with requests. Yeehaw!
Bjoern
 
Posts: 484
Joined: Fri Jan 06, 2012 11:00 pm
Location: TXL (RIP)
Version: Next
OS: ArchLinux

Re: Write value to specific vector index

Postby Hooray » Mon Jan 27, 2020 10:24 pm

just briefly, your code looks fine - the style you're using is actually consistent with more advanced programming concepts (e.g. use of anonymous functions) - you could simplify the code a little by moving the node/path out of the loop body into a string variable, and then use that variable instead.
Also, no need for string concatenation - i..e getprop() supports an optional index argument, too (see the wiki for details)

Note that you will want to stop the timer, to prevent it from running too often.

One simple option would be testing the whole thing as part of a Canvas GUI dialog and then register an event handler to remove the timer once the dialog is closed.

For any logging, you can just create a simple wrapper "loginfo()" func and then use print/printlog/logprint or tooltips as needed - that way, you only have to touch a single single, the loop body would only call your wrapper

For the conditional check, you could introduce a helper function "inRange()" - again, only relevant if you are concerned about readability.

Apart from that, it seems that your Lua background comes in handy already :-)
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: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: Write value to specific vector index

Postby Bjoern » Mon Jan 27, 2020 11:19 pm

Hooray wrote in Mon Jan 27, 2020 10:24 pm:just briefly, your code looks fine - the style you're using is actually consistent with more advanced programming concepts (e.g. use of anonymous functions) - you could simplify the code a little by moving the node/path out of the loop body into a string variable, and then use that variable instead.


Yes, i've already thought of that, too.

Also, no need for string concatenation - i..e getprop() supports an optional index argument, too (see the wiki for details)


This?
Code: Select all
getprop("/sim/view", i, "name")


Note that you will want to stop the timer, to prevent it from running too often.


That's a question i wanted to ask as well. Can I kill the timer from inside the timer function?

One simple option would be testing the whole thing as part of a Canvas GUI dialog and then register an event handler to remove the timer once the dialog is closed.


As in "manually closed"?
Not my preferred option. i want the dialog to kill itself once input values are nominal again.

For any logging, you can just create a simple wrapper "loginfo()" func and then use print/printlog/logprint or tooltips as needed - that way, you only have to touch a single single, the loop body would only call your wrapper


I'm thinking about a file io based debug log as the Nasal console tends to swallow my outputs too fast and CTRL+F'ing through fgfs.log is...tedious.

For the conditional check, you could introduce a helper function "inRange()" - again, only relevant if you are concerned about readability.


Not sure about that. My rule of thumb is that a function is only required when absolutely necessary or used more than once.

Apart from that, it seems that your Lua background comes in handy already :-)


But only after finding out that I can get away with doing things Lua style in Nasal as well.
The listener had me a bit confused as I really thought that this was the only means of getting a property's value without flooding FG to death.

One thing I still need to figure out is whether doing setprop() every frame will also flood FG. If it does, I'd just force pause&freeze the sim until the input controller is moved and then unpause&unfreeze it.


By the way:
This is a simple Lua script for X-Plane. It gets the current day from the OS and then adjusts the dataref (=property) within X-Plane to match that day before going into idle mode. "Do_sometimes" is just the plugin's short version for a ten second timer.
In Lua, variables are assumed to be global unless specifically declared local. Dataref_table is the only "getprop"-like method that will permit local dataref (property) access (table = vector in Nasal). Global access may cause issues or even simulator crashes when written to by two different scripts. Nasal is much more safe and simple in that regard, handling access to variables from another script on what would amount to communication between two modules in Lua.

Also note the absence of any end marks for statements and curly brackets.

Code: Select all
local day_os=(os.date("%j") - 1) -- XP starts day count at zero, therefor os count needs to be adjusted
local day_sim=dataref_table("sim/time/local_date_days")

local date_synched = 0

function Datesync()
if date_synched == 0 then
   print("Initial OS Day is "..day_os..", sim day is "..day_sim[0])
   if day_os ~= day_sim[0] then
      day_sim[0] = day_os
      print("OS day and sim day synched. ("..day_sim[0].."/"..day_os..")")
   end
   date_synched = 1
end
end
do_sometimes("Datesync()")


You could maybe indeed write a Nasal-based Lua parser for such simple scripts but there's still way too many special cases (like the aforementioned timer) that would require extensive conversion work.
Bjoern
 
Posts: 484
Joined: Fri Jan 06, 2012 11:00 pm
Location: TXL (RIP)
Version: Next
OS: ArchLinux

Re: Write value to specific vector index

Postby Hooray » Tue Jan 28, 2020 6:31 pm

Yes, you could stop/kill the timer, you would just want to store the timer object in a corresponding variable that is accessible from the callback (i.e. outer scope), that's also what is commonly done for listeners that are single-shot only.

And like you say, you can also close the dialog automatically, or just use a tooltip instead (wiki)

The file based I/O will work, too -but if you are concerned about in-sim use, you could log to a ScrollArea - see Canvas Snippets and/or the Nasal REPL, which is using the same widget.
Basically, it's a scroll-able text area, you can make it read-only and append your logging there.




PS: May I suggest to turn this topic/discussion into a new tutorial for the wiki ?`That's a great way to get started improving our docs, and since you are new to "Nasal", it's more likely that you manage to write something that might be of interest to newcomers.
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: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: Write value to specific vector index

Postby Bjoern » Tue Jan 28, 2020 11:43 pm

Hooray wrote in Tue Jan 28, 2020 6:31 pm:Yes, you could stop/kill the timer, you would just want to store the timer object in a corresponding variable that is accessible from the callback (i.e. outer scope), that's also what is commonly done for listeners that are single-shot only.


Thanks, I'm going to investigate this.

And like you say, you can also close the dialog automatically, or just use a tooltip instead (wiki)


Canvas is my preference as tooltips are automatically killed after a certain time.

The file based I/O will work, too -but if you are concerned about in-sim use, you could log to a ScrollArea - see Canvas Snippets and/or the Nasal REPL, which is using the same widget.
Basically, it's a scroll-able text area, you can make it read-only and append your logging there.


That's good to know for potential future projects.

PS: May I suggest to turn this topic/discussion into a new tutorial for the wiki ?`That's a great way to get started improving our docs, and since you are new to "Nasal", it's more likely that you manage to write something that might be of interest to newcomers.


While a good idea in principle, a new tutorial would only add clutter to an already very cluttered wiki section. A few sentences and and a link to the finished script in an "example scripts" section and proper code commenting might just do a better job.
What I think would be more useful to newcomers is a generally more streamlined information presentation throughout the entire Nasal section, starting from a table of content in the Nasal core article to complement the rollouts to the right (a page with tables is easier to search by CTRL+F), to articles branching out from a superficial explanation of a concept or command down to a more complex explanation of the core concept or inner working. To minimize confusion, all references to deprecated concepts or commands (e.g. settimer()), despite the warning boxes, should be degraded to passing notes, as I assume the most relevant demographic for running Nasal runs at least the second latest release anyway. I also dislike using quotations to convey information as these distract from the technical aspect of the information (a link to the source should suffice if one needs a citation).
But I think that's best discussed in a new thread.

For reference and if I remember corrctly, there was a huge cleanup of the aircraft section some time ago and I think the guys and gals did a great job in terms of structure and presentation. There's an overview page that branches out into more detailed subpages. Very easy and convenient to use and I can still obtain all the information I need.
Bjoern
 
Posts: 484
Joined: Fri Jan 06, 2012 11:00 pm
Location: TXL (RIP)
Version: Next
OS: ArchLinux


Return to Nasal

Who is online

Users browsing this forum: No registered users and 5 guests