Lua scripts

All scripts in states, namely the entry, exit and action scripts, are written in the Lua language. Lua is a widely used and well documented extension language. Thus, this chapter is not intended to be a thorough documentation of the Lua language itself but assumes that the reader is already familiar with the language. Instead, this document provides information on core model data types and functions that are provided for Lua scripts to interact with the core model state machine logic, access parameters, variables and results and call operations. The currently used Lua interpreter is of version 5.3.5.

Accessing core model properties

This section documents the core model related data exposed by the core in Lua scripts.

Parameter, result and variable values

States are parameterized with their parameter value and can provide computation results through their result value. The state's parameter value in Lua scripts is bound to the symbol parameter while the state's current result value is bound to the symbol result. Additionally, every state can utilize a variable to store private data which is persistent even if a (child) state is deactivated and later activated again. The state's variable is bound to the variable symbol. Its parameter value corresponds to the declared parameter type of the state, the result value corresponds to the declared result type and the variable to the declared variable type. Values are translated into (and from) Lua data types according to the following rules:

  • int and float types correspond to numbers.

  • string types correspond to strings.

  • bool types correspond to booleans.

  • Array types (e.g. [2]int) correspond to Lua arrays, i.e. tables with ascending numeric keys starting with 1.

  • Struct types correspond to Lua tables with keys according to the struct field names.

  • State interface types correspond to Lua tables with exactly two fields, "state" and "parameter", just like the corresponding LF value described in: Parameter, result, variable values

  • A nil value corresponds to Lua nil.

In addition, the functions setResult and setVariable are provided by the core to allow to set the current result or variable to a new value. setResult and setVariable allows to set the result value only partially or to set it to nil. A partial result/variable value is only defined for struct result/variable types and is a Lua table that contains a subset of the keys of the declared result type. Fields which cannot be found in the result/variable type struct lead to a runtime error.

Let us assume a state with the following result type:

{
   int foo;
   float bar;
   []int baz;
}

The following code snippets show different examples on how to set the result value (the same is true for setVariable and the variable value):

setResult({ foo = 1, bar = 2.4, baz = {1, 2, 3} }) -- 1. Result value: { foo: 1; bar: 2.4; baz: [1, 2, 3]; }
setResult({ foo = 1 })                             -- 2. Result value: { foo: 1; bar: nil; baz: nil }
setResult(nil)                                     -- 3. Result value: nil
setResult({ foo = true })                          -- 4. Runtime error: invalid type for 'foo'.
setResult({ foo = 1.3 })                           -- 5. Runtime error: invalid type for 'foo'.

As can be seen, example 1 sets a complete result value. Example 2 sets only one field in the result. Example 3 shows resetting of the complete result value while both examples 4 and 5 lead to a runtime error because the values true and 1.3 are not of type int.

Accessing ports

To query the truth value of ports, the core exposes the function port(name) in the Lua script context. The function returns a boolean value corresponding to the truth value of the corresponding port's expression.

Accessing children

An array of the names of all children of the current core model state is bound to the symbol childNames. In addition, Lua scripts can access the activation state and result and parameter values of their direct children with the child function. However, grand children cannot be accessed.

The child function takes one parameter, the name of the child, and returns a Lua table containing the following properties:

  • active: A boolean indicating if the child is currently active.

  • parameter: The child's parameter value. Parameter expressions are resolved when a state is activated, so if the child has never been active, this value will be nil.

  • result: The child's result value. If the child did not set a result yet, this value will be nil.

  • port(name): The truth value of the corresponding port of the child.

Caution

The Lua table also contains some properties that are experimental and might have breaking changes in the future:

  • setParameterOverride(child, value): Set a value that will override the parameter value of the child obtained from resolving its parameter expression when the child is activated. For structs in the parameter type, partial values are allowed and will be merged into the parameter value. Fields which cannot be found in a parameter type struct lead to a runtime error.

  • resetParameterOverride(child): Removes any existing parameter override.

  • parameter_override: The child's parameter override value. If no override has been set, this value will be nil.

The following example shows how to find all currently active children:

local active = {}
for i, childName in ipairs(childNames) do
  if child(childName).active then
    active[#active + 1] = childName
  end
end

Hint

Functions taking a child as their first parameter can be called using the syntax child:function(arg), which is syntactic sugar for child.function(child, arg) in Lua.

Note

Variable values can only be accessed by a state itself and not using the child() function.

Services, operations and events

To interact with services, the core exposes the function service(name). It takes exactly one parameter, the name of the service to access. The return value of the service function is a table that contains two functions, operation(name) and event(name). The name parameter is either the name of the operation or the name of the event to access.

The event function returns the latest event value sent by the corresponding service or nil if none has been received yet. The value corresponds to the declared event type and is translated to a Lua value according to the same rules that are used for translating parameter and result values.

The operation function returns a table with the following fields:

  • call(request): A function to call the operation. The parameter must be a Lua value that corresponds to the declared operation request type. The function does not have a meaningful return value. However, the call fails if a call to the same operation is already pending, i.e. if the operation has been called in this state in this macro step.

  • status: A string indicating the status of a previous operation call. One of the following values;

    • "unknown": The operation has not been called.

    • "pending": The operation has been called but no result has been received yet.

    • "success": The operation call succeeded and the result field contains the result value.

    • "error": The operation call failed and the error field contains the corresponding error value returned by the service.

    • "internalError": An internal error occurred. Neither a result nor an error value could be received.

  • result: The result value of a successful operation call or nil.

  • error: The error value returned by a failed operation call or nil.

The core manages operation calls on a per-state basis. In other words, operation calls are tracked separately for each state. This allows to call the same operation in the same macro step from multiple states. The individual correct result values are exposed to the corresponding calling states. Result values stay valid for a complete macro step. In particular that means that if a state called an operation in a previous macro step and received a result in this macro step, the result will be available even if the state makes another call. The result will however be invalidated in the next macro step if for the new operation call, no result has been received so far.

Results stay valid for a complete macro step. Between macro steps, results are kept as long as a state is active and until the state calls the operation again.

Third-party Lua modules

The core provides support for third-party Lua modules that provide additional functionality. Currently, only a matrix module is provided (also refer to the github page of the matrix module for more details).

Modules can be loaded with Lua's require function. The following example shows how to load and use the matrix module:

matrix = require("matrix")
foo = matrix {1, 2, 3, 4}
foo_inverted = matrix.invert(foo)

Logging data

For simple debugging, the core provides the two functions print and printf that allow to log data on a specific logger of the core. It must be noted that both functions always print a newline as the last character. Multiline printing is possible by using \n in strings.

The print function takes an arbitrary number of parameters and prints them.

The printf function prints according to a format string. The format string is a string with arbitrary characters and sequences of format sequences. Format sequences are strings that begin with a percent character (%). The following format sequences are supported:

  • %1, %2, ..., %9: Print the first, second, ..., ninth parameter after the format string.

  • %{n}: Print the nth parameter after the format string.

  • %p: Print the parameter value of this state.

  • %r: Print the current result value of this state.

  • %%: Print the % character.