Lua Stimulator

Summary

Doc_BoxAlgorithm_LuaStimulator.png
  • Plugin name : Lua Stimulator
  • Version : 1.1
  • Author : Yann Renard
  • Company : INRIA
  • Short description : Generates some stimulations according to an Lua script
  • Documentation template generation date : Dec 30 2016

Description

This box gives a fast control over stimulation streams thanks to Lua scripts.

Scripts are very simple programming languages. They are usually easy to understand, fast to write and reasonably fast to execute. They are used for high level operations and do not need compilation. There are a number of scripting languages around. Lua is one of those scripting languages. It is know for its execution speed and it's ease of use. It can be used on a large number of platforms. For all those reasons, Lua is widely used in the video game industry. If you want more information about Lua, feel free to visit their website at http://www.lua.org and on their documentation website http://www.lua.org/manual/5.1

This box has a variable number of inputs and outputs. The author is able to add as many of them as necessary. The box automatically decodes the incoming streams and automatically encodes the outgoing streams, leaving the interesting part to the script : doing something with input stimulations and produce output stimulations.

The script should be divided in three functions :

The whole script behavior should be executed in a single execution of the process function. Thanks to this, the script code is made a lot more easy to understand than the usual C++ boxes. Of course, in case it is necessary, the script can yield, letting the kernel perform a new step of execution.

A simple API is proposed allowing the script to :

During the process function, you can also :

NOTE: people using the deprecated unstable API would be interested in the Upgrading from the unstable API section.

initialize(box)

This function is called by the box at IBox::initialize phase. It is up to the script developer to implement this function. In case you don't want to do anything at initialization phase, just ignore this function.

  • the box parameter enables the script developer to access the API described hereafter.

uninitialize(box)

This function is called by the box at IBox::uninitialize phase. It is up to the script developer to implement this function. In case you don't want to do anything at uninitialization phase, just ignore this function.

  • the box parameter enables the script developer to access the API described hereafter.

process(box)

This function is called by the box at the first IBox::process phase. It is up to the script developer to implement this function and this function is mandatory. It is executed only once, meaning that if that code must be run on a long duration, it must release some CPU cycles using the box:sleep() function.

  • the box parameter enables the script developer to access the API described hereafter. Among others, it will let the script get some usefull information about the box topology (number of inputs, number of outputs, number of settings etc...)
  • the box parameter also enables the script developer to access all the runtime functions such as receiving stimulations, sending stimulations etc...

box:get_input_count()

This function returns the number of inputs the box has.

box:get_output_count()

This function returns the number of outputs the box has.

box:get_setting_count()

This function returns the number of settings the box has, including the script filename itself.

box:get_setting(setting_index)

This function returns a string containing the value of a given setting in the box. This setting value may be expanded using the configuration manager thanks to a box:get_config(token) call.

  • setting_index can range from 1 to n if the box has n settings.

box:log(log_level, line_of_text)

This function sends a string to the log manager. This message may be printed in the console for the user or may be popped up to him if this is important enough. In any case, it will appear in the log file.

  • log_level the log level which you want to use to print this line of text. This is represented in string and can take the following values : Debug, Trace, Info, Warning, ImportantWarning, Error and Fatal. If you use an invalid value, then Debug is used.
  • line_of_text can be any line of text you want to print

box:set_filter_mode(active)

By default the Lua scripting box works in a clock-driven mode: it runs with a certain frequency, and each time the process code is run, it outputs a stimulus chunk. When the filter mode is active (=1), the input stimulation stream drives the output chunk generation instead. In filter mode, you can copy input stimulations to the output while keeping their exact timestamps.

Explanation: Lua box generates a stimulation chunk stream [t1,t2[, [t2,t3[, [t3,t4[, ... , with a high frequency (small chunks). The t are the start and end times of each chunk. However, the input stream may have chunks with a bigger granularity such as [t1,t3[, [t3,t5[, ..., and so on. Now if the box receives stimulation tagged at time t1 from such a bigger granularity input stream, it will have already sent out the chunk [t1,t2] as the current time by then is at least t3 as chunk [t1,t3[ was received. Hence the stimulation at t1 must be dropped as OpenViBE stimulation stream guarantees that if a stimuli tagged at time t has not been received by the time a chunk with its beginning timestamp greater than t is received, there will never be a stimuli at t.

Note that for the filter mode operation it is necessary that the upstream generates a meaningful stimulation stream. If not, the box will not output stimulations when the filter mode is active.

box:get_config(token)

This function uses the configuration manager to expand a configuration token. The configuration token are listed in the openvibe.conf file and can contain valuable information about the OpenViBE environment.

  • token can be any string containing configuration variables (e.g. ${Path_Samples}/my-experiment).

box:get_current_time()

This function returns the elapsed time in seconds since the scenario started. The returned value is a float.

  • WARNING: this function should only be called while in the process(box) callback.

box:get_stimulation_count(input_index)

This function returns the number of pending stimulation on a specific input given with input_index.

  • input_index can range from 1 to n if the box has n inputs.
  • NOTE: if you call this function on an unexisting input, it returns 0.
  • WARNING: this function should only be called while in the process(box) callback.

box:get_stimulation(input_index, stimulation_index)

This function returns the details of a specific pending stimulation for a specific input given with input_index.

  • input_index can range from 1 to n if the box has n inputs.
  • stimulation_index can range from 1 to m if the valid input input_index has m inputs.

The result is in the form of a 3-tuple containing :

  • the stimulation code as an integer
  • the stimulation date as a float
  • the stimulation duration as a float

If you call this function with an stimulation_index bigger than what get_stimulation_count would have returned for the input_index input, then the Lua script sleeps until enough stimulation arrived and this function can return.

  • WARNING: if you call this function on an unexisting input, the behavior is undefined
  • WARNING: this function should only be called while in the process(box) callback.

box:remove_stimulation(input_index, stimulation_index)

Removes a pending stimulation on a specific input given with input_index.

  • input_index can range from 1 to n if the box has n inputs.
  • stimulation_index can range from 1 to m if the valid input input_index has m inputs.

If you call this function with an stimulation_index bigger than what get_stimulation_count would have returned for the input_index input, then the Lua script sleeps until enough stimulation arrived and this function can return.

After this call, the pending stimulations order is changed, stimulations stimulation_index+1, stimulation_index+2, ... being moved to stimulation_index, stimulation_index+1, ... respectively.

  • WARNING: this function should only be called while in the process(box) callback.

box:send_stimulation(output_index, stimulation_identifier, stimulation_date, opt:stimulation_duration=0)

Sends a stimulation with code stimulation_identifier, date stimulation_date and duration stimulation_duration on output

  • output_index.
  • output_index can range from 1 to n if the box has n outputs.
  • stimulation_identifier is an integer with the identifier of the stimulation to send. See Doc_Stimulations for stimulation codes.
  • stimulation_date is a float (in seconds) with the date of the stimulation. The box will take care of buffering the stimulation if needed before actually sending it. However, you can't send a stimulation in "the past".
  • stimulation_duration is a float (in seconds) with the duration of the stimulation. It is usually unused so if you don't know what to put here, just ignore this parameter, or let it be 0.
  • WARNING: this function should only be called while in the process(box) callback.

box:sleep()

Pauses the script execution. The box then continues its execution, so the kernel can perform an additional step. Next time the box is executed, it will restore the script execution so you can eventually continue or sleep again.

  • NOTE: The current time is affected by this call !
  • WARNING: this function should only be called while in the process(box) callback.

If you want to wait until a fixed date, just use this simple Lua function :

function wait_until(box, time)
while box:get_current_time() < time do
box:sleep()
end
end

If you want to wait for a fixed duration, just use this simple Lua function :

function wait_for(box, duration)
wait_until(box, box:get_current_time() + duration)
end

Outputs

1. Stimulations

  • Type identifier : Stimulations (0x6f752dd0, 0x082a321e)

Settings

The box can have a varying number of settings. The first setting is a fixed but the author can add as many settings as he wants. Those settings can be retrieved in the Lua script thanks to the box:get_setting(setting_index) function

1. Lua Script

This setting defines what Lua script should be executed.

  • Type identifier : Script (0xb0d0db45, 0x49cbc34a)
  • Default value : [ ]

Examples

Here is a sample script

-- this function is called when the box is initialized
function initialize(box)
box:log("Trace", "initialize has been called")
dofile(box:get_config("${Path_Data}") .. "/plugins/stimulation/lua-stimulator-stim-codes.lua")
-- inspects the box topology
box:log("Info", string.format("box has %i input(s)", box:get_input_count()))
box:log("Info", string.format("box has %i output(s)", box:get_output_count()))
box:log("Info", string.format("box has %i setting(s)", box:get_setting_count()))
for i = 1, box:get_setting_count() do
box:log("Info", string.format(" - setting %i has value [%s]", i, box:get_setting(i)))
end
end
-- this function is called when the box is uninitialized
function uninitialize(box)
box:log("Trace", "uninitialize has been called")
end
-- this function is called once by the box
function process(box)
box:log("Trace", "process has been called")
-- enters infinite loop
-- cpu will be released with a call to sleep
-- at the end of the loop
while box:keep_processing() do
-- gets current simulated time
t = box:get_current_time()
-- loops on all inputs of the box
for input = 1, box:get_input_count() do
-- loops on every received stimulation for a given input
for stimulation = 1, box:get_stimulation_count(input) do
-- gets the received stimulation
identifier, date, duration = box:get_stimulation(input, 1)
-- logs the received stimulation
box:log("Trace", string.format("At time %f on input %i got stimulation id:%s date:%s duration:%s", t, input, identifier, date, duration))
-- discards it
box:remove_stimulation(input, 1)
-- triggers a new OVTK_StimulationId_Label_00 stimulation five seconds after
box:send_stimulation(1, OVTK_StimulationId_Label_00, t+5, 0)
end
end
-- releases cpu
box:sleep()
end
end

Miscellaneous

Upgrading from the unstable API

The Lua box has been flagged as unstable for months for several reasons :

  • The API was incomplete.
  • There were some potential issues in the chosen names for the API functions.
  • The box crashed in some circumstances.

The box has been stabilized now and this results in some small changes in the API. Basically, the initialize(box), uninitialize(box) and process(box) callbacks are passed a box parameter which should be used to call the API functions. Each API function is now prefixed with box: as described in the Object-Oriented Access documentation page - http://www.lua.org/pil/28.3.html

In case you would not like to modify your already existing scripts in depth, I suggest you just include the lua-stimulator-legacy.lua script at the initialization of your existing scripts using the following command

dofile(box:get_config("${Path_Data}") .. "/plugins/stimulation/lua-stimulator-legacy.lua")

Using OpenViBE stimulation codes in Lua scripts

It could be convenient to have the stimulation names available in a Lua script. For this purpose, I suggest you just include the lua-stimulator-stim-codes.lua script at the initialization of your existing scripts using the following command

dofile(box:get_config("${Path_Data}") .. "/plugins/stimulation/lua-stimulator-stim-codes.lua")