Definition section

The definition section is a text area. Per default, it is located to the left of the canvas. You can move or resize it like other elements on the canvas.

In the definition section, you have to define entities you want to use in your statechart. This includes variables, events, and operations. Variables and events have to be defined in the scope of a named interface or the internal interface. Especially useful for larger parts of a statechart model is the possibility to use namespaces.

When it comes to code generation all these elements are properly reflected in the generated source code as far as the respective target language supports it.

Scopes

Namespaces

The statechart language allows to define unique namespaces. They can be used to qualify references to statechart elements.

namespace trafficlights

Interface scopes

Declarations in the interface scope are externally visible. They can be shared within the environment, e.g., client code that uses the state machine.

interface NamedInterface:
  in event event1
  out event event3 : integer
  var variable1 : integer

Please note: All elements defined in a named interface must be referenced using that interface’s name, such as NamedInterface.event1, NamedInterface.event3, or NamedInterface.variable1.

It is also possible to have a single unnamed interface, which is externally visible as well:

interface:
  in event event1

It behaves exactly as a named interface.

Internal scope

Definitions made in the internal scope are visible within the statechart only, but not to the outside. It is not possible to access them from client code.

internal:
  var localVariable1: integer
  event localEvent: integer
  operation localOperation (int1 : integer, int2 : integer): integer
  localEvent2 / raise NamedInterface.event3 :
  localOperation(valueof(localEvent) , NamedInterface.variable1)

Variables

Variables have to be defined in an internal or external interface. Variables in the internal scope are not visible to any client code.

var variable1: real

Variables that are defined in a named interface have to be referenced using the interface name, see section "Interfaces".

Variables are always typed, see section "Types".

Variables can be marked by the readonly keyword, which has the effect that client code can only read them. Please note that this restriction is imposed on the client code only. Readonly variables can still be modified from within the statechart.

var readonly pi: real = 3.1415

Constants

A variable can be immutable, i.e., constant. Such variables are marked by the const keyword. They can neither be modified by the client code nor from within the statechart.

const variable1: real

Events

An event is something of importance that happens at a certain point in time in the context of a state machine. For example, a user pushes a button, a temperature sensor delivers a value, a period of time has passed, etc. An event can be of one of three basic types:

  • Internal events are meant to be raised and processed by the state machine internally only.
  • External events are defined in an interface scope and are either incoming or outgoing.

Events that are defined in a named interface need to be referenced using the interface’s name, see section "Interfaces".

Events can be processed in triggers, see section "Trigger specification". In order to raise an event in the state machine, see section "Raising an event". For details on the processing of events see section "Raising and processing an event".

Incoming and outgoing events

An event in an interface scope has a direction. It is either incoming or outgoing.

  • An incoming event is raised somewhere in the environment, delivered to the state machine, and processed by the latter.
  • An outgoing event is raised within the state machine and delivered to the outside.

In an event declaration, the corresponding keywords are in and out, followed by the keyword event, followed by the event’s name.

interface NamedInterface:
  in event event1
  out event event2

Events in the internal scope do neither enter nor leave the state machine, thus they don’t have a direction.

internal:
  event ev1

Events with values

An event can be typed and can carry a value:

internal:
  event event1 : integer

Read access to an event’s value is possible in the statechart using the valueof() built-in method, see section "Built-in methods" for details.

Please note: Reading an event’s value is possible only when the event actually occurs, for example in a guard condition.

Example (reading an event’s value in a transition’s guard condition):

event1 [valueof(event1) == 6]

An event parameter can be specified when raising an event, as in the following example:

raise event1 : 3+3

Regarding the syntax of raising an event, see section "Raising an event". Regarding the more complicated details of processing an event, see section "Raising and processing an event".

Operations

An operation connects a state machine to the outside world by making external behaviour accessible to the state machine.

A state machine typically interacts with the outside world, in order, for example, to read data from a sensor, engage an actuator, etc. The client software has to provide such behaviour as functions or methods in a programming language the state machine can interact with. An operation is the statechart language’s means to call such an external procedure from the state machine.

Consider, for example, a C function float read_sensor() that should be called by the state machine. To interface with that function, an operation read_sensor must first be defined in the state machine, and can then be called from, e.g., a state or transition. At execution time, a call to the read_sensor operation is mapped to an actual call of the external C function with the same name. To make this work, the statechart must have been generated as source code of the respective target language, of course.

It is the purpose of a code generator to create a suitable construct in the respective target language for the procedure call. For details on code generation, please see section "Generating state machine code".

Operations can have none, one, or multiple parameters. A parameter is declared with a name and a type. An operation may have a single or no return type similar to usual programming languages. Please see section "Types" for details.

operation myOperation (xValue: integer, yValue: integer): integer

You can call myOperation by using positional parameters:

myOperation(27, 42)

This call assigns the value 27 to the operation’s xValue parameter and 42 to yValue.

Alternatively, you can use named parameters, like in the following example:

myOperation(xValue = 27, yValue = 42)

Named parameters make their order irrelevant. The following call is semantically equivalent to the one above:

myOperation(yValue = 42, xValue = 27)

While an operation call with named parameters is longer than the equivalent call with positional parameters, named parameters are a great means for self-documenting code (especially if the parameter names are more telling than in the example above).

Operations with a variable number of parameters are supported, too. To be more exact, it is possible to specify an operation’s last parameter as being allowed to occur an arbitrary number of times. This feature is usually called „varargs”, short for „variable number of arguments”. Compared to a „regular” operation parameter declaration, the varargs parameter name is followed by three dots ( ...) to indicate it may occur zero or more times.

For example, an operation sum adding a variable number of summands and returning their sum could be defined as follows:

operation sum(count: integer, summand...: integer): integer

Sample calls of this operation are sum(1, 42), sum(2, 4711, 815), sum(7, 555, 338, 881, 192, 69, 999, 610), or even sum(0). In this example the first parameter advises the called function or method of how many instances of the last parameter it should expect. Whether such information is needed or not depends on the target language. For example, a C function needs to receive this information while a Java method does not.

Annotations

Annotations are metadata that you can add to the definition section of your statechart. They provide a means to control specific aspects of the execution semantics.

@CycleBased

The @CycleBased annotation specifies that the cycle-based execution scheme is to be used.

Synopsis: @CycleBased( period)

The mandatory parameter period indicates the suggested period of time between two successive run-to-completion steps in milliseconds. Only the statechart simulator and the SCTUnit testing framework take the period value into account, however. It is neither of significance to nor reflected in the generated code, and thus it remains the client code’s responsibility to explicitly call runCycle() – and to decide when to do so.

If the definition section contains neither the @CycleBased nor the @EventDriven annotation, the state machine will behave as if @CycleBased(200) would have been coded.

Example: The definition section below specifies that the state machine should behave according to the cycle-based execution scheme with an interval of 100 milliseconds between two cycles.

@CycleBased(100)

interface:
    in event e

@EventDriven

The @EventDriven annotation specifies that the event-driven execution scheme is to be used.

Synopsis: @EventDriven

If the definition section contains neither the @CycleBased nor the @EventDriven annotation, the state machine will behave as if @CycleBased(200) would have been coded.

Example: The definition section below specifies that the state machine should behave according to the event-driven execution scheme.

@EventDriven

interface:
    in event e

@ChildFirstExecution

The @ChildFirstExecution annotation specifies that the state machine should always execute the substates („children”) of an active composite state first, before executing the composite state („parent”) itself. Please see section "Parent-first versus child-first execution" for details and examples.

Synopsis: @ChildFirstExecution

Please note that @ParentFirstExecution specifies the opposite behaviour. Both @ChildFirstExecution and @ParentFirstExecution are global settings for all composite states.

If the definition section contains neither the @ParentFirstExecution nor the @ChildFirstExecution annotation, the state machine will behave as if @ParentFirstExecution would have been coded.

@ParentFirstExecution

The @ParentFirstExecution annotation specifies that the state machine should always first execute an active composite state („parent”) itself, before executing its substates („children”). Please see section "Parent-first versus child-first execution" for details and examples.

Synopsis: @ParentFirstExecution

Please note that @ChildFirstExecution specifies the opposite behaviour. Both @ChildFirstExecution and @ParentFirstExecution are global settings for all composite states.

If the definition section contains neither the @ParentFirstExecution nor the @ChildFirstExecution annotation, the state machine will behave as if @ParentFirstExecution would have been coded.

@SuperStep

The @SuperStep annotation enables the superstep semantic for a run-to-completion step. In contrast to a regular step, a superstep executes all valid subsequent state transitions.

Take a look at the example model below:

Example model for superstep semantic

Without the superstep semantic, an incoming event e causes only one state transition, e.g. from state A to state B. However, when superstep semantic is enabled, raising event e causes a state transition from A over B to C. As there is no other outgoing transition from C, the local reaction is also executed, hence x is set to 42.

You can think of the superstep semantic as a loop which performs a regular step until no state is entered in the regular step. See the following pseudo-code (a regular step is called microStep here):

do {
    stateEntered = false
    microStep()
} while (stateEntered)

Please note, the flag stateEntered is set to true inside a microStep whenever a state is entered.

@SuperStep and @EventDriven

In combination with the event-driven execution semantic things get slightly more complicated, as internal events are queued and need to be also processed. Internal events are processed after incoming events. Internal events are also processed with the superstep semantic. Take a look at the corresponding pseudo code (the loop from the previous code snippet is called superStepLoop here):

superStepLoop()
while (!eventQueue.isEmpty) {
    event = eventQueue.pop
    superStepLoop()
}

To sum up, let’s take a look at a slightly more complicated example for the event-driven case:

Example model for superstep semantic in event-driven statechart

What happens when state A is active and event e is raised? First, event e invokes a superstep like in the previous example. While taking the state transition to B and C, the local events local1 and local2 are put into the internal event queue. However, they are not visible until the processing of event e is finished. That means that the local reaction in state C is executed because no outgoing transition can be taken. Hence, x is set to 42. Afterwards, the internal event queue is processed, event by event, starting with local1. This again invokes a superstep and state L is entered. As with each self-transition, state L is exited and re-entered again, the superstep loop continues until x equals to 17. Next, internal event local2 is processed and state C is entered. The local reaction in state C is not executed, as event e is no more raised.