This page documents both basic and advanced use of the RTT Lua bindings by example. More formal API documentation is available here.

Important!

As of orocos toolchain-2.6 the deployment component launched by rttlua has been renamed from deployer to Deployer. This is to remove the differences between the classical deployer and rttlua and to facilitate portable deployment scripts. This page has been updated to use the new, uppercase name. If you are using an orocos toolchain version prior to 2.6, replace use "deployer" instead.

What is this RTT-Lua stuff anyway?

Lua is a simple, small and efficient scripting language. The Lua RTT bindings provide access to most of the RTT API from the Lua language. Use-cases are:

writing deployment scripts in Lua

writing Lua components and services

using the rFSM Statecharts with RTT

To this end RTT-Lua consists of:

a Lua scriptable taskbrowser (rttlua-gnulinux etc. binaries)

a standard RTT Component which can be scripted with Lua

a RTT service which can extend existing components with Lua scripting

Most information here is valid for all three approaches. If not, this is explicitly mentioned. The listings are shown as interactively entered into the rttlua- REPL (read-eval-print loop), but could just the same be stored in a script file.

Getting started

Compiling

Currently RTT-Lua is in OCL. Is is enabled by default but will only be built if the Lua-5.1 dependency (Debian: liblua5.1-0-dev, liblua5.1-0, lua5.1) is found.

CMake options:

BUILD_LUA_RTT: enable this to build the rttlua shell, the Lua component, and the Lua plugin.

BUILD_LUA_RTT_DYNAMIC_MODULES: (EXPERIMENTAL) build RTT and deployer as pure Lua plugins. Not recommended unless you know what you are doing.

BUILD_LUA_TESTCOMP: build a simple testcomponent that is used for testing the bindings. Not required for normal operation.

Setting up the path to rttlib

rttlib.lua is a Lua module, which is not strictly necessary, but highly recommended to load as it adds various syntactic shortcuts and pretty printing (Many examples on this page will not work without!). The easiest way to load it is to setup the LUA_PATH variable:

exportLUA_PATH=";;$HOME/src/git/orocos/ocl/lua/modules/?.lua"

If you are a orocos_toolchain_ros user and do not want to hardcode the path like this, you can source the following script in your .bashrc:

Where's my TaskContext?

Here:

> tc = rtt.getTC()

Above code calls the getTC() function, which returns the current TC and stores it in a variable 'tc'. For showing the interface just write =tc. In the repl the equal sign is a shortcut for 'return', which in turn causes the variable to be printed. (BTW: This works for displaying any variable)

Initalization of array/sequence types

When you created an RTT array type, the initial length will be zero. You must set the length of an array before you can assign elements to it (starting from toolchain-2.5 fromtab will do this automatically:

collect() returns multiple arguments: first a SendStatus string ('SendSuccess', 'SendFailure') followed by zero to many output arguments of the operation.

collect blocks until the operation was executed, collectIfDone() will immediately return (but possibly with 'SendNotReady')

If your code make excessive use of "Sending Operations" something in your application design is probably wrong.

Can I define new Operations from Lua?

Answer: No.

Workaround: define a new TaskContext that inherits from LuaComponent and add the Operation there. Implement the necessary glue between C++ and Lua by hand (not hard, but some manual work required).

Is it possible to call a connected OperationCaller?

Answer: No (but potentially it would be easy to add. Ask on the ML).

Services

Loading and Using

For example, to load the marshalling service in a component and then to use it to write a property (cpf) file:

> tc=rtt.getTC()> depl=tc:getPeer("Deployer")> depl:loadService("lua","marshalling")-- load the marshalling service in the lua componenttrue>=tc:provides("marshalling"):writeProperties("props.cpf")true

A second (and slightly faster) option is to get the Operation before calling it:

>-- get the writeProperties operation ...> writeProps=tc:provides("marshalling"):getOperation("writeProperties")>=writeProps("props.cpf")-- and call it to write the properties to a file.true

Note: The -i option makes rttlua enter interactive mode (the REPL) after executing the script. Without it would exit after finishing executing the script, which in this case is probably not what you want.

How to write a RTT-Lua component

A Lua component is created by loading a Lua-script implementing zero or more TaskContext hooks in a OCL::LuaComponent. The following RTT hooks are currently supported:

bool configureHook()

bool activateHook()

bool startHook()

void updateHook()

void stopHook()

void cleanupHook()

void errorHook()

All hooks are optional, but if implemented they must return the correct return value (if not void of course). It is also important to declare them as global (by not adding a local keyword. Otherwise they would be garbage collected and not called)

The following code implements a simple consumer component with an event-triggered input port:

require("rttlib")
tc=rtt.getTC();-- The Lua component starts its life in PreOperational, so-- configureHook can be used to set stuff up.function configureHook()
inport = rtt.InputPort("string","inport")-- global variable!
tc:addEventPort(inport)
cnt =0returntrueend-- all hooks are optional!--function startHook() return true endfunction updateHook()local fs, data = inport:read()
rtt.log("data received: "..tostring(data)..", flowstatus: ".. fs)end-- Ports and properties are the only elements which are not-- automatically cleaned up. This means this must be done manually for-- long living components:function cleanupHook()
tc:removePort("inport")
inport:delete()end

How to write a RTT-Lua Service

In contrast to Components (which typically contain functionality which is standalone), Services are useful for extending functionality of existing Components. The LuaService permits to execute arbitrary Lua programs in the context of a Component.

Simple example

The following dummy example loads the LuaService into a HelloWorld component and then runs a script that modifies a property:

require"rttlib"
tc=rtt.getTC()
d = tc:getPeer("Deployer")-- create a HelloWorld component
d:loadComponent("hello","OCL::HelloWorld")
hello = d:getPeer("hello")-- load Lua service into the HelloWorld Component
d:loadService("hello","Lua")-- Execute the following Lua script (defined a multiline string) in-- the service. This dummy examples simply modifies the Property. For-- large programs it might be better tostore the program in a separate-- file and use the exec_file operation instead.
proggie =[[require("rttlib")
tc=rtt.getTC()-- this is the Hello Component
prop = tc:getProperty("the_property")
prop:set("hullo from the lua service!")]]
prop = hello:getProperty("the_property")-- get hello.the_propertyprint("the_property before service call:", prop)
hello:provides("Lua"):exec_str(proggie)-- execute program in the serviceprint("the_property after service call: ", prop)

Executing a LuaService function at the frequency of the host component

More useful than just running once is to be able to execute a function synchronously with the updateHook of the host component. This can be achieved by registering a ExecutionEngine hook (much easier than it sounds!).

The following Lua service code implements a simple monitor that tracks the currently active (TaskContext) state of the component in whose context it is running. When the state changes the new state is written to a port "tc_state", which is added to the context TC.

This code could be useful for a supervision statemachine that can then easily react to this state change by means of an event triggered port.

require"rttlib"
tc=rtt.getTC()
d = tc:getPeer("Deployer")-- create a HelloWorld component
d:loadComponent("hello","OCL::HelloWorld")
hello = d:getPeer("hello")-- load Lua service into the HelloWorld Component
d:loadService("hello","Lua")
mon_state =[[-- service-eehook.luarequire("rttlib")
tc=rtt.getTC()-- this is the Hello Component
last_state ="not-running"
out = rtt.OutputPort("string")
tc:addPort(out,"tc_state","currently active state of TaskContext")function check_state()local cur_state = tc:getState()if cur_state ~= last_state then
out:write(cur_state)
last_state = cur_state
endreturntrue-- returning false will disable EEHookend-- register check_state function to be called periodically and-- enable it. Important: variables like eehook below or the-- function check_state which shall not be garbage-collected-- after the first run must be declared global (by not declaring-- them local with the local keyword)
eehook=rtt.EEHook('check_state')
eehook:enable()]]-- execute the mon_state program
hello:provides("Lua"):exec_str(mon_state)

Note: the -i option causes rttlua to go to interactive mode after executing the script (and not exiting afterwards).

How to perform runtime system validation?

It is often useful to validate a deployed system at runtime, however you want to avoid cluttering individual components with non-functional validation code. Here's what to do (Please also see this post on orocos-users, which inspired the following)

Use-case: check for unconnected input ports

1. Write a function to validate a single component

The following function accepts a TaskContext as an argument and checks wether it has unconnected input ports. If yes it prints an error.

2. After deployment, execute the validation function on all components:

This can be done using the mappeers function.

rttlib.mappeers(check_inport_conn, depl)

The mappeers function is a special variant of map which calls the function given as a first argument on all peers reachable from a TaskContext (given as a second argument). We pass the Deployer here, which typically knows all components.

It is a best-practice to split the initalization (setting up required functions, peers or ports used by the fsm) and the fsm model itself into two files. This way the fsm model is kept as platform independent and hence reusable as possible.

The following initalization file is executed in the newly create LuaComponent for preparing the environment for the statemachine, that is loaded and initalized in configureHook.

launch_fsm.lua

require"rttlib"require"rfsm"require"rfsm_rtt"require"rfsmpp"local tc=rtt.getTC();local fsm
local fqn_out, events_in
function configureHook()-- load state machine
fsm = rfsm.init(rfsm.load("fsm.lua"))-- enable state entry and exit dbg output
fsm.dbg=rfsmpp.gen_dbgcolor("rfsm-rtt-example",{ STATE_ENTER=true, STATE_EXIT=true},false)-- redirect rFSM output to rtt log
fsm.info=function(...) rtt.logl('Info',table.concat({...},' '))end
fsm.warn=function(...) rtt.logl('Warning',table.concat({...},' '))end
fsm.err=function(...) rtt.logl('Error',table.concat({...},' '))end-- the following creates a string input port, adds it as a event-- driven port to the Taskcontext. The third line generates a-- getevents function which returns all data on the current port as-- events. This function is called by the rFSM core to check for-- new events.
events_in = rtt.InputPort("string")
tc:addEventPort(events_in,"events","rFSM event input port")
fsm.getevents = rfsm_rtt.gen_read_str_events(events_in)-- optional: create a string port to which the currently active-- state of the FSM will be written. gen_write_fqn generates a-- function suitable to be added to the rFSM step hook to do this.
fqn_out = rtt.OutputPort("string")
tc:addPort(fqn_out,"rFSM_cur_fqn","current active rFSM state")
rfsm.post_step_hook_add(fsm, rfsm_rtt.gen_write_fqn(fqn_out))returntrueendfunction updateHook() rfsm.run(fsm)endfunction cleanupHook()-- cleanup the created ports.
rttlib.tc_cleanup()end

The last line means the following: launch fsm in <fsmfile> in service identified by execstr_op, true: create an execution engine hook so that the rfsm.step is called at the component frequency. (See the generated rfsm_rtt API docs).

Replacing states, functions and transitions of an existing FSM model

rFSM allows the creation of a FSM by loading a parent FSM into a new .lua file. This way, it is possible to add, delete and override states, transitions and functions. Though powerful, these operations can make the new FSM fairly hard to track. In this regard, a few tricks can make our life easier:

naming states and transitions in a consistent way

making the parent FSM as simple as possible with meaningful transition events

overriding a full state is less confusing than overriding a single entry or exit function

Generally speaking, the most effective way of creating a new FSM from a parent one is populating the original simple states by overriding them with composite states. In this context, the parent FSM provides “empty” boxes to be filled with application-specific code.

In the following example, “daughter_fsm.lua” loads “mother_fsm.lua” and overrides a state, two transitions and a function. “daughter_fsm.lua” is launched by a Lua Orocos component named “fsm_launcher.lua” . Deployment is done by “deploy.ops” . Instructions on how to run the example follow.

One-liner to build a table of peers

A Coordinator often needs to interact with many or all other components in its vicinity. To avoid having to write peer1 = depl:getPeer("peer1") all over, you can use the following function to generate a table of peers which are reachable from a certain component (commonly the deployer):

peertab = rttlib.mappeers(function(tc)return tc end, depl)

Assume the Deployer has two peers "robot" and "controller", they can be accessed as follows:

How are types converted between RTT and Lua?

Lua has to work with two typesystems: its own and the RTT typesystem. To makes this as smooth as possible the basic RTT types are automatically converted to their corresponding Lua types as shown by the table below:

RTT

Lua

bool

boolean

float

number

double

number

uint

number

int

number

char

string

string

string

void

nil

This conversion is done in both directions: basic values read from ports or basic return values of operation are converted to Lua; vice versa if an operation with basic Lua values is called these will automatically be converted to the corresponding RTT types.

How to add a custom pretty printing function for a new type?

In short: write a function which accepts a lua table representation of you data type and returns either a table or a string. Assign it to rttlib.var_pp.mytype, where mytype is the value returned by the var:getType() method. That's all!

How to use classical OCL Deployers ? (like with Corba, or with a Taskbrowser)

If you are used to manage your application with the classic OCL Taskbrowser or if you want your application to be connected via Corba, you may only use lua for deployment, and continue to use your former deployer. To do so, you have to load the lua service into your favorite deployer (deployer, cdeployer, deployer-corba, ...) and then call your deployment script.

and with yourLuaDeploymentFile.lua containing the kind of stuff described in this Cookbook. Like the one in paragraph "How to write a deployment script"

How to generate graphical representations of rFSM models

The rfsm-viz command allows you to generate easy-to-read pictures representing the structure of your FSM model. This tool uses the rfsm2uml and fsm2dbg modules and requires the libgv-lua package. Practically: