Statechart language reference

The statechart language is used to describe statecharts. It consists both of graphical and of textual elements.

State machine basics

Words and definitions

A finite-state machine (FSM) or finite-state automaton (FSA, plural: automata), finite automaton, or simply a state machine as a mathematical model of computation. It is an abstract machine that can be in exactly one of a finite number of states at any given time. The FSM can change from one state to another in response to some external inputs; the change from one state to another is called a transition. An FSM is defined by a list of its states, its initial state, and the conditions for each transition.

A state diagram is one of many possible representations of an FSM. A state transition table would be different way to describe an FSM.

A statechart or Harel statechart is an extended form of the classic state diagram. The basic principles are the same, however. In this documentation, the term statechart usually denotes the graphical representation of an FSM.

The term state machine is a short form of finite-state machine. In this documentation, the term state machine usually pertains to the dynamic aspects of an FSM, i.e. it is more thought of as being executed, simulated etc. than being depicted.

Automata theory

YAKINDU Statechart Tools was designed to create statecharts according to David Harel’s statechart theory. Harel statecharts have become part of the Unified Modeling Language (UML). Harel introduced a couple of extensions to conventional state diagrams, making them more concise and more readable.

Classic state diagrams

Classic state diagrams describe finite-state machines and consist of the following things only:

  • A (finite) set of possible states
  • A start state
  • An accepting or final state
  • A set of transitions, connecting states
  • A set of input symbols
  • A set of output symbols
  • An output function, mapping pairs of input symbols and states to output symbols

These parameters describe a finite-state machine completely.

Moore and Mealy machines

In classic automata theory, two distinct types of finite-state machines exist:

  • The Moore machine , where the output of the machine depends on its current state only.
  • The Mealy machine , where the output of the machine depends on its current state and its input.

It is possible to transform these types of machine into each other, however, states, transitions and the output function need to be changed to achieve this.

Harel statecharts

Harel statecharts extend the classic state diagrams by a couple of additional aspects, resulting in representations that need much less states and transitions, making them much more compact, expressive, manageable and comprehensible – though not as mathematically concise as Moore or Mealy machines.

Harel added the following notations, dealing with hierarchy, concurrency, and communication:

  • Composite states and regions, containing nested states or regions.
  • History states, allowing to re-enter a composite state or region at the point where it had been left.
  • Orthogonal states, allowing to run state machines concurrently.
  • Events, allowing for communication between orthogonal states.
  • Variables, allowing to memorize values and reducing the necessary number of states drastically.
  • Actions (output) can not only occur along transitions, but additionally inside of states, especially as entry actions or exit actions.
  • Activities (operations), bridging the gap between a state machine and real-world behavior.

Since Harel statecharts are a superset of Mealy and Moore machines, it is possible to model all of these types in YAKINDU Statechart Tools.

Statechart model

A statechart or statechart model consists of several components.

The most important part is the canvas, comprising one or more top-level regions. Regions in turn are containing states and transitions. For hints on how to use the statechart editor to modify a statechart, see section "Editing statecharts".

The definition section is a textual statechart model component. It contains definitions of namespaces, interfaces, variables, events, operations, etc.

Most objects defined in the definition section have a type. For example, a variable could be of the integer type. Another example is the type of the return value (if any) of an operation. Events can be typed as well.

A type system or domain specifies which types are available. YAKINDU Statechart Tools has a built-in type system. Section "Types" lists the types that are available in YAKINDU Statechart Tools' standard domain.

YAKINDU Statechart Tools Professional Edition allows for additional domains to be installed. It includes domains for the following languages:

  • C
  • TypeScript

Statechart elements

Regions

YAKINDU statecharts are organized in regions. Hence it is possible to organize multiple state machines in different regions and to run them virtually concurrently.

Parallel regions

Parallel regions

Regions typically contain states and transitions. A state in turn can contain one or more regions, turning it into a composite state (contains one region) or a orthogonal state (contains two or more regions).

States

States are the central elements of a state machine. A state has to be placed inside a region and must have a name that is unique witin this region. During state machine execution, a state is either active or passive.

A state can have behavior. The behavior specifies which actions are executed on which conditions. Such actions can be triggered by entering the state, leaving the state, occurrence of events, conditions becoming true, or time passing. The behavior specification is a text in a state’s box and consists of a sequence of local reactions .

The state Countdown in the sample statechart below has three local reactions:


State example

State example: A simple state machine to launch a rocket.

When the Countdown state is entered, it sets the counting variable t to 10. As long as that variable’s value is greater than 0, each second it gets counted down by 1. The operation speak provides audible information on how many seconds to go until the rocket is launched. If t has been counted down to 0 – checked by a transition’s guard condition –, the Countdown state will become inactive and launch the rocket upon exiting. The countdown can be aborted anytime before launch by a cancel event. (See figure "Transition example" for a different state machine which solves the same task but puts more emphasis on transitions.)

The state’s behavior contains a subtle error. Can you spot it?

Transitions

A transition is the transfer of one state to another. A transition is diagrammed as an arrow leading from a source state to a target state. When a transition is taken, the source state ceases to be active, and the target state becomes active instead.

A transition should have a reaction . A transition’s reaction specifies

  • what the conditions for this transition to be taken are, and
  • which actions, if any, are to be executed when the transition is taken.

The occurrence of an event, a condition becoming true, or time passing can trigger a transition. The transition’s reaction is attached to the transition’s arrow as a text. Its syntax can be found in the section on reactions.

Please note: Transitions without a reaction are possible, but useless. They will never be taken.

Here’s an example:


Transition example

Transition example: A simple state machine to launch a rocket.

The initial transition’s effect sets the counting variable t to 10. As long as that variable’s value is greater than 0, every second it gets counted down by 1. The operation speak – executed as one of a transition’s actions – cares for audible information on how many seconds to go until the rocket is launched. If t has been counted down to 0 – checked by a transition’s guard condition –, the rocket is launched. The countdown can be aborted anytime before launch by a cancel event which triggers the appropriate transition to fire. (See figure "State example" for a different state machine which solves the same task, but puts more emphasis on states.)

Figure "Transition syntax overview" shows how the syntax of a transition reaction is defined.


Transition syntax overview

Transition syntax overview

Entry and exit points

A standard UML composite state is always entered via its initial state. The latter denotes the inner state that is to be activated, unless a history state tells otherwise. Since there can be only a single initial state per region, a standard UML composite state always starts at the same inner state (history states aside).

Unlike UML composite states, however, composite states in YAKINDU statecharts can additionally denote specific named entry points. A transition leading to a composite state can specify the entry point to be used instead of the initial state.

Analogously, a composite state can have named exit points. Transitions with that composite state as their source state may specify which exit point to react to, and thus lead to different target states, depending on which of the source composite state’s exit points has been taken.

The syntax to connect a transition to an entry point with the name entry-point-1 of a target composite state is as follows:

# >entry-point-1

A named exit point can be used as follows:

# exit-point-1> [ exit-point-2> ]…

It is also possible to specify both entry and exit points in a single transition specification. The order of exit and entry points is irrelevant. Instead, the position of the > character is decisive:

  • If the > character is to the right of a name, like in exit_name>, that name denotes an exit point.
  • If the > character is to the left of a name, like in >entry_name, that name denotes an entry point.

The sample statechart in figure "Entry and exit points" contains two composite states:

  • The composite state Process models a process with two passes (states) A and B. If both passes succeed, the composite state is left via the no_problem exit point. However, if an error occurs in either A or B, the execution flow proceeds to the problem exit point and leaves the composite state there.
  • The composite state Handle result is intended to handle the processing result, be it success or failure. It has two entry points success and failure.

The question is how to connect the exit points of Process to the corresponding entry points of Handle result. This is done by two transitions leading from Process to Handle result and appropriate specifications.

The transition shown on the left specifies # no_problem> >success. This means: If the source composite state is left via the no_problem exit point, then enter the target composite state at the success entry point. The specification of the transition on the right is analogous: If the source state is left via the problem exit point, then enter the target state at the failure entry point.

Alternatively, Process could have been modeled with two different error exit points, say error_1 and error_2. This would allow to respond differently to different error conditions, while still enabling to catch them both with a single flow. A transition with # >error_1 >error_2 problem> would do so.


Entry and exit points

Entry and exit points

The entry or exit points to take have to be specified at the right-hand side of a transistion specification. The corresponding syntax element is the TransitionProperties non-terminal symbol that is shown in figure "Transition syntax overview". It is part of the TransitionReaction.

More on entry and exit points can be found in sections "Entry point" and "Exit point".

Transition priorities

If a state has several outgoing transitions, several of these transitions could in principle fire at the same time, dependent on the events that occured and on guard conditions.

Consider this example:


Transition priorities [1]

Transition priorities [1]

Which transition will be taken if the state machine is in state A and event e1 occurs?

Fortunately YAKINDU state machines behave deterministically, so this question can be answered unambiguously. Outgoing transitions of a state have a definite order and are prioritized by that order. The state machine checks the transitions one by one in this order and executes the first transition that fulfills all prerequisites for firing.

As you can see in figure "Transition priorities [1]", little numbers at the transition arrows indicate their priorities. This numbering is shown only if the option Show transition priority in Window → Preferences → YAKINDU SCT → Diagram Appearance has been activated. By default, this option is not activated.

The order of transitions is a property of their source state. In the example above, the transition priorities of state A appear like this in the properties view:


Transition priorities [2]

Transition priorities [2]

To change priorities, select a transition in the properties view and click on the up or down button as appropriate.

Entry points

When a state machine starts or when the control flow enters a region, an entry point, or simply entry, defines which state is to be activated first. The flow passes through the state machine’s or the region’s entry point and transitions to the target state the entry point’s transition is pointing to. This state becomes active.

An entry point has a single outgoing transition and no incoming ones. Its outgoing transition has neither a trigger nor a guard condition. It is always taken immediately.

An entry point is depicted as a filled circle, see figure "Entry point, exit point, and final state".


Entry point, exit point, and final state

Entry point, exit point, and final state

Default entry point and initial state

An entry point without a name is called the default entry point. It is similar to the UML’s initial state. However, while the transition from the initial state to an ordinary state may specify a guard, the transition sourced at an entry point cannot.

A region may have at most one default entry point.

Named entry points

The default entry point specifies a single entry point into a region. A region can provide a multitude of additional or alternative entry points, thus implementing different kinds of behavior, depending on which entry point has been taken. If a region comprises multiple entry points, these entry points must have names. An entry point’s name must be unique within its region.

The default entry point implicitly has the name default. Alternatively, it is possible to explicitly give the name default to the default entry point. Semantically both variants are equivalent. Whether an entry point has no name or has the name default, in both cases this entry point is called the default entry point.

An incoming transition to a composite state can specify the named entry point it shall lead to. For details, see section "Entry and Exit points".

Named entry points have no equivalent in the UML.

The sample statechart in figure "Entry point" has a composite state named Handle result. This composite state has a default entry point as well as an entry point called failure. If state A is active and the error trigger fires, control is transitioned to the Handle result composite state. The notation error # >failure specifies that the failure entry point is to be used; see section "Entry and exit points".


Entry point

Entry point

Exit points

An exit point, or simply exit, is a pseudo state that is used to leave and deactivate a composite state. Exit points are counterpart to entry point. See section "Final state" for a different way to terminate a region or state machine.

An exit point may have multiple incoming transitions and has no outgoing one.

An exit point is depicted as an unfilled circle with an X-shaped cross, see figure "Entry point, exit point, and final state".

Within a region, multiple exit points are allowed. Each exit point must either have a name that is unique within its region or be the default exit point. The default exit point is either unnamed or has the name default, which is semantically equivalent. A region may have at most one default exit point. For details on how to connect named exit points to transitions, see section "Entry and exit points".

When the control flow of a composite state reaches an exit point in any of its regions, all states in other regions of that composite state, if any, are deactivated immediately. The composite state will be left and be deactivated. It maintains no status information that could be probed from the outside. In other words, reaching an exit point in one of a composite state’s regions has severe consequences for all the other regions, since they are exited and disposed immediately. After that, the containing composite state is also exited. There must be an unguarded transition that catches the exit and leads to another state outside of the composite state. The semantics of an exit point is different from that of a final state; please see section "Final state" for details.

Named exit points have no equivalent in the UML.

Final state

A final state denotes the end of the execution flow of a state machine or region. See section "Exit point" for a different way to terminate a composite state.

A final state is depicted as an unfilled circle with a smaller filled black circle inside, see figure "Entry point, exit point, and final state".

A final state can have multiple incoming transitions and has no outgoing ones.

Within a region, only a single final state is allowed, but each region may have its own final state.

When a region reaches its final state, control stops there and waits until all other orthogonal regions, if any, have reached their respective final states, too. The semantics of final states is different from that of exits; please see section "Exit point" for details.

Please note: In YAKINDU Statechart Tools, final states are proper states. This is different from the UML, where a final state is a pseudo state, i.e. in the UML a final state cannot have any properties that normal states can have, except for a name.

Choices

A choice is a pseudo state. It can be used to model a conditional path. A choice node divides a transition into multiple parts.

Usually the first transition points towards the choice node. One of the choice’s outgoing transitions can carry a condition.

Synchronizations

A synchronization is a means to either split a flow into several parallel regions of a substate or to join several parallel flows from orthogonal regions into a single flow.

The synchronization state corresponds to the UML’s concepts of fork and join. Whether a synchronization state behaves as a fork or as a join depends on its usage.

  • A synchronization with a single incoming transition and several outgoing ones forks the flow into several regions of the same substate. These regions are executed in parallel, see section "Orthogonal states".
  • A synchronization with several incoming transitions from parallel regions of the same substate and a single outgoing transition joins the incoming flows into a single flow of execution.

A synchronization state is shown as a thick horizontal or vertical line, as can be seen in figure "Synchronization state".

For a synchronization to actually join the incoming transitions and execute the outgoing one, all of the following conditions must be met:

  • All source states of all transitions leading to the synchronization must be active.
  • All guard conditions that are specified in all these transitions must be fulfilled.
  • If one or more triggers are defined, at least one trigger must fire at a point in time while the conditions above are met.

Figure "Synchronization state" shows a sample statechart containing a forking and a joining synchronization. After having left the Initialize state, the synchronization state forks the execution flow into two regions r1 and r2. Both are part of the Process composite state and both are executed virtually concurrently. That is, when activating Process, the substates Line A 1 and Line B 1 also become active. When the flows continues and both Line A 2 and Line B 2 have been reached, the synchronization state on the right-hand side joins the flows and transitions to substates Cleanup, making it the active state. However, as long as only one of Line A 2 and Line B 2 is active, the synchronization will wait for the other one to also become active, before proceeding to Cleanup.

Synchronization state

Synchronization state

The example also demonstrates different lengths and orientations of the synchronization symbol. In the statechart editor, first select the synchronization symbol, then use a handle in one of the symbol’s corners to change length or orientation. The handles in the middle of the symbol have no effect.

Composite states

A composite state is a state containing a region. All usual characteristics of regions apply – they can have entry points, exit points, and so on. Transitions can connect inner states of composite states to states that are at the same hierarchical level as the composite state, or they can directly be sourced at the composite states. This is useful for constructs like „cancel”, where the operation of a composite state should be able to be aborted every time, no matter the active substate.

See the sections on entry point, exit point, final state and connecting entry points and exit points to transitions for further information and examples.

Orthogonal states

An orthogonal state is basically a composite state with more than one region. These regions are executed virtually concurrently. Please note the word virtually! YAKINDU Statechart Tools does not guarantee in any way that orthogonal regions are really executed concurrently. At the moment, no code generator utilizes threads to achieve this. Orthogonal states should rather be understood as a manner to have two or more sub-statecharts working together, however, they are executed one after the other other in every cycle, and in a defined order: top to bottom, left to right. The same applies to multiple regions in the statechart itself. Please consult section "Raising and processing an event" for further information on region priorities and their meanings for the statechart execution.

The presumably most famous example for orthogonal states is the keyboard example:

Orthogonal state

Orthogonal states

Shallow history states

A shallow history state is a pseudo state that is placed inside the region of a composite state. It is used to remember the last active state inside a composite state. This makes it possible to jump back to the remembered state instead of starting at the inner initial state again.

A history state saves the current state exactly when its containing region is left. A shallow history state saves the state of the current level only. If you need to save the status of multiple nested composite states, use a deep history state.

The following example, showing the answering of a questionaire, explains this:

Shallow history [1]

Shallow history [1]

Particularly interesting in this statechart are the checkProgress and goon events. The checkProgress event jumps back to the init state while assigning the current progress count to the variable temp. The event goon jumps to the shallow history state that was placed inside the composite state.

Shallow history [2]

Shallow history [2]

Shallow history [3]

Shallow history [3]

When the goon event is triggered, the most recent active state inside of the composite state answeringQuestions is activated again.

Deep history states

A deep history state is similar to a shallow history state, but more complex. With a deep history state, the latest status of multiple nested states is remembered.

A history state saves the current state exactly when its containing region is left. A deep history state saves the status of multiple nested composite states. If saving the state of the current level only is sufficient, consider using a shallow history state instead.

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 readonly (constants):

var readonly pi: real = 3.1415

Variables can be referenced by the environment.

var external variable3: integer = 34

Constants

A variable can be immutable, i.e. constant. For this special variable the keyword const is used:

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

An event can have a value assignment:

internal:
  event event1: integer = 25

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 first execute the substates („children”) of an active composite state, before executing the composite state („parent”) itself. Should the execution of a substate trigger a transition leading away from the composite state, the latter and any remaining not-yet-executed substates will not be executed, except for any exit actions.

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.

Example 1a:

Consider the statechart in the figure below. Composite state A and its substate B are active and the @ChildFirstExecution annotation has been specified in the definition section.


Child-first, example 1a, before transition

Child-first, example 1a, before transition

If event e occurs, the state machine will consider child state B first, find a matching transition, and thus immediately proceed to state D, see the figure below.


Child-first, example 1a, after transition

Child-first, example 1a, after transition

Example 1b:

Consider the statechart in the figure below. Composite state A and its substate B are active and the @ParentFirstExecution annotation has been specified in the definition section.


Parent-first, example 1b, before transition

Parent-first, example 1b, before transition

If event e occurs, the state machine will consider parent state A first, find a matching transition, and thus immediately proceed to state C, see the figure below.


Parent-first, example 1b, after transition

Parent-first, example 1b, after transition

@ParentFirstExecution

The @ParentFirstExecution annotation specifies that the state machine should always first execute an active composite state („parent”) itself, before executing its substates („children”). Should the execution of the composite state trigger a transition leading away from the parent, its substates will not be executed, except for any exit actions.

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.

Please see section "@ChildFirstExecution" for examples.

Reactions

Reactions are one of the most important features of the YAKINDU statechart language. Basically everything that happens in a statechart happens in a reaction.

Reactions can be attached to states as well as to transitions. While states can have as many reactions as you wish, transitions can only have one.

The syntax follows the standards of automata theory, where the output is put into relation to the input of the automaton using the following notation (especially Mealy automata):

input / output

YAKINDU Statechart Tools uses the following syntax for all reactions:

trigger [ guard ] / effect

The possible values that can be specified as a trigger depend on the context, which is either a state or a transition.

All three components are grammatically optional, though a reaction with neither a trigger nor a guard will never happen. Reactions with no effect are useful for transitions, the slash / can be omitted as well then.

Trigger specifications

Reactions can define a trigger that needs to occur to activate the reaction. Events are possible triggers, as well as certain keywords defined in the statechart language.

Triggers can be combined with guards to further define the execution conditions of the reaction. In that case, the reaction is only executed if both the trigger occurs and the guard condition is true.

You can define multiple triggers for a reaction, separated by comma: trigger1, trigger2. For the combined trigger to fire, it is sufficient that one of the individual triggers occurs.

There are a couple of special triggers defined by the statechart language:

Examples:

  • A reaction shall occur if an event named ev1 happens: ev1 / effect
  • A reaction shall occur if ev1 or ev2 happen: ev1, ev2 / effect
  • A reaction shall occur if ev1 happened or three seconds passed: ev1, after 3s / effect

Figure "Event specifications syntax overview" shows where to put event specifications in a reaction trigger and how their syntax is defined.


Event specifications syntax overview

Event specifications syntax overview

Reaction trigger "after"

The after trigger specifies a one-shot time event.

After the specified period of time the reaction is triggered. An after trigger can be used in transitions of states as well as in local reactions of states and statecharts. The specified period of time starts when the state or statechart is entered.

after 20 s

Synopsis: after time unit

The time value is a integer literal or an expression with an integer value.

The time unit is one of:

  • s: seconds
  • ms: milliseconds
  • us: microseconds
  • ns: nanoseconds

Reaction trigger "every"

The every trigger specifies periodic time events.

The reaction is triggered recurrently after each passing of the specified period of time. An every trigger can be used in transitions as well as in local reactions of states and statecharts. The specified period of time starts when the state or statechart is entered and repeats periodically.

every 200 ms

Synopsis: every time unit

The time value is a integer literal or an expression with an integer value.

The time unit is one of:

  • s: seconds
  • ms: milliseconds
  • us: microseconds
  • ns: nanoseconds

Reaction trigger "always"

The always trigger is always true and enables a reaction to be executed in every run-to-completion step (RTC). This trigger is equivalent to the oncycle trigger.

Reaction trigger "oncycle"

The oncycle trigger is always true and enables a reaction to be executed in every run-to-completion step (RTC). This trigger is equivalent to the always trigger.

Reaction trigger "else"

The else trigger is intended to be used for the outgoing transitions of choice pseudo states to make sure that there is always an outgoing transition that can be taken. It can only be be used in transitions and implies the lowest evaluation priority for that transition. The default trigger is equivalent to the else trigger.

Reaction trigger "default"

The default trigger is intended to be used for the outgoing transitions of choice pseudo states to make sure that there is always an outgoing transition that can be taken. It can only be be used in transitions and implies the lowest evaluation priority for that transition. The default trigger is equivalent to the else trigger.

Reaction trigger "entry"

An entry trigger marks actions that are carried out when entering a state or state machine, immediately before making it active.

Please note: A self-transition, leading from a state back to itself, actually first leaves and then re-enters the state. That means that its exit effects as well as its entry effects are executed. This is in contrast to a local reaction, which is executed without leaving the active state. Thus exit and entry effects are not executed in the latter case.

Reaction trigger "exit"

An exit trigger marks actions that are carried out when exiting a state or state machine, immediately after making it inactive.

Please note: A self-transition, leading from a state back to itself, actually first leaves and then re-enters the state. That means that its exit effects as well as its entry effects are executed. This is in contrast to a local reaction, which is executed without leaving the active state. Thus exit and entry effects are not executed in the latter case.

Guard conditions

A guard condition, or simply guard, is another option that can be used to specify when a reaction should be executed. Guard conditions can be used on their own or in conjunction with a specified trigger . In the latter case, the trigger needs to be fired and the guard needs to be true for the reaction to fire its effect.

As a reminder, the complete reaction syntax is as follows:

trigger [ guard ] / effect

In the square brackets you can use any expression that evaluates to a boolean. Boolean variables, all kinds of comparisons, and operations returning a boolean fall into this category.

Please note: YAKINDU Statechart Tools does not support the concept of „truthy and falsey” seen in C, Javascript or Python. [x] is not a valid guard condition, unless the type of x is boolean.

Please consult figure "Guard condition syntax overview" to find out how a guard condition syntactically fits into a reaction.


Guard condition syntax overview

Guard condition syntax overview

Actions and reaction effects

A transition specification may contain zero, one or more actions that are executed when the transition is taken. An action could be something like assigning a value to a variable, calling an operation, or raising an event. All actions taken together are called the transition’s reaction effect, or simply effect. The complete syntax for reactions is as follows:

trigger [ guard ] / effect

This means that the effect will only be executed if the trigger fires or does not exist and the guard condition evaluates to true or does not exist and at least a trigger or a guard exist. See the sections about triggers and guard conditions.

Normally, actions are statements. Note that you can also use the conditional expression to make some constructs more elegant.

If an effect shall consist of multiple actions, they must be separated by a semicolon. That means the last action is not followed by a semicolon. The syntax is:

trigger [ guard ] / action1 ; action2 ; action3 ;

A reaction in a state always has a reaction effect with at least one action.

Figure "Reaction effect syntax" shows the syntax definition.


Reaction effect syntax

Reaction effect syntax

Example: When a transition or local reaction specified as ev1 [x > 3] / x += 1; notify(x); raise ev2 is executed, it increments variable x by 1, calls the notify operation with x as parameter, and raises event ev2.

Raising and processing an event

For the syntax of event raising, see section "Raising an event".
For the syntax of event declaration, see section "Events".

When a reaction raises an event, the subsequent behaviour of the state machine is highly dependent on the execution scheme, selected by either the @CycleBased (default) or the @EventDriven annotation. In short:

  • In the cycle-based execution scheme, the raised event will be added to the events that are processed by the current run-to-completion step. However, it will only be visible „downstream” in the run cycle, i.e. active states that have not yet been executed can take the event into account. When the run cycle is completed, the event will cease to exist. It can thus not be processed in any subsequent run-to-completion step.
  • In the event-driven execution scheme, the raised event will be added to an event queue. As a consequence, it will not be visible „downstream” in the current run cycle. Instead, the event will trigger a run cycle on its own. In that run-to-completion step, it will be visible everywhere.

The following sections will give a more in-depth view on these different behaviours.

Processing an event in the cycle-based execution scheme

As explained above, an event that has been raised using the raise statement exists in the current run-to-completion step only and thus can be dealt with during the rest of that particular run-to-completion step only.

Please also note that orthogonal regions are executed sequentially and in a deterministic order.

This has remarkable consequences on where an event is „visible” and where it is not. Let’s say we have a state machine at a certain point in time where n orthogonal regions are active. The regions ri have a defined execution order: r1r2 → … → rn-1rn.

If an event e is raised in region rm, then e is available only in those regions that have not been processed yet, i. e. in ri where i > m and in.

The following example illustrates this. When the state machine is started, the situation is as shown in figure "Raising an event [1]": A and X in the parallel regions r1 and r2 are active states. Important: Region r1 is executed before r2.


Raising an event [1]

Raising an event [1]

Now event e1 occurs. It triggers transition A → B in r1. This transition’s effect raise e2 causes event e2 to be raised. This event triggers transition X → Y in r2 to fire within the same RTC.

After that, the situation is now as can be seen in figure "Raising an event [2]": substates B and Y are now active states. However, the transition B → C will not be taken, because that would have to happen in another RTC. But by then the particular e2 instance raised by A → B will be long gone, since it was restricted to the RTC it has been raised in.


Raising an event [2]

Raising an event [2]

Orthogonal regions within a composite state are always executed from left to right or from top to bottom, respectively. On the top level, where orthogonal regions can be graphically arranged at will, the execution order is defined by the statechart model’s property region priority. To inspect or change it, open the properties view and select the statechart’s definition section.

The region priorities of the example above are shown in figure "Region priorities". Change the execution order and the example will behave differently than described.


Region priorities

Region priorities

Not being able to raise an event and process it in „earlier” regions is a restriction you can overcome using variables. The idea is to set a „communication variable” to a certain value in a region that is processed „later”. During the next RTC the earlier region could act on the variables value. Another option – and in many cases the better on, is to change to the event-driven execution scheme.

Processing an event in the event-driven execution scheme

As explained above, an event that has been raised using the raise statement is appended to an event queue, and thus it is not visible in the current run-to-completion step (RTC). Since in the event-driven execution scheme an RTC always processes a single event at a time, only the event that caused the RTC to execute in the first place remains visible „downstream” in the current RTC.

The raised event will, however, trigger an RTC on its own. In that RTC, it will be visible to all regions, no matter of their execution order. The execution order as explained in the preceeding section still applies, but it is of less importance in the event-driven approach.

Events raised internally by the raise statement in the reaction of a state or transition take precedence over events that entered the state machine from the outside. This convention ensures that an external event together with all effects it causes – and in particular with all events raised internally – are processed completely, before the next external event can take charge.

Consider, for example, events a and b having entered, in that order, a state machine that is executing in event-driven mode. Since a entered the state machine first, it is processed first in an RTC. Event b is put on a queue for external events, where it has to wait until a is done. Processing of a, however, leads to another event c being raised via the raise statement. Since a is still being processed, c is, similar to b, also queued.

However, c is put on a different event queue, a queue that is reserved for internal events only. Events in the internal event queue are always processed first. The external event queue will be considered only if the internal event queue is empty. As a consequence for our example, the order of events being processed by successive run-to-completion steps is a, c, b.

Please note: There is only a single internal event queue, and all internally-raised events are appended to it. Let’s consider the example above again and let’s assume that processing of a not only raised the internal event c, but also an additional event d. Since both c and d are going to the internal event queue, the overall processing sequence is a, c, d, b. Now let’s also assume that in the course of processing c yet another event e is raised. Like all internally-raised events, it will be appended to the end of the internal event queue, i.e. behind d, not behind c, resulting in an overall execution sequence of a, c, d, e, b. In other words, the state machine is taking a breadth-first approach on internally raised events, in lieu of depth-first.

Please also note: In event-driven execution mode, there’s a looming risk of endless event processing you should be aware of. In the example above, imagine processing of event c would always raise another instance of c. In that case, the internal event queue would never get empty, the state machine would be busily processing one c after the other, without ever ending and without ever getting to b.

Types

The YAKINDU statechart language has a small integrated type system consisting of the following simple types:

  • integer
  • real
  • boolean
  • string
  • void

Statements

A statement is one of three kinds:

  • assignment
  • event raising
  • operation call

Assignments

The language has the following assignment operators:

simple assignment =
multiply and assign *=
divide and assign /=
calculate modulo and assign %=
add and assign +=
subtract and assign -=
bitshift left and assign <<=
bitshift right and assign >>=
bitwise AND and assign &=
bitwise XOR and assign ^=
bitwise OR and assign |=

Raising an event

An event is raised by using the keyword raise, followed by the event name, e. g. raise incoming_call. If the event is an interface event, the name of the interface must be prepended, using dot notation.

Example:

interface intrfc:
  out event ev

Usage in the statechart:

always / raise intrfc.ev

See also sections "Declaring events" and "Raising and processing an event".

Calling an operation

An operation is called similarly to other programming languages using the operation name and passing actual parameter values in parenthesis and separated by comma. The parameters can be expressions. The parenthesis can be omitted when no parameters are passed. Please see section "Operations" for an example.

Expressions

Expressions in the YAKINDU statechart language are similar to expressions in other programming languages. The language provides operators for logical expressions, number arithmetic, bitwise arithmetic, and bit shifting.

The type of a logical expression is boolean.

Logical AND

expr1 && expr2

Logical OR

expr1 ||  expr2

Logical NOT

!expr1

Conditional expression

expr1 ? expr2 : expr3

If expr1 evaluates to true, this returns expr2, else exp3. This can be used to assign values to variables:

y = x > 5 ? 1 : 2

This does the same as the following snippet of C code:

if(x > 5) {
  y = 1;
} else {
  y = 2;
}

Bitwise XOR

expr1 ^ expr2

Bitwise OR

expr1 | expr2

Bitwise AND

expr1 & expr2

Logical relations and shift operators

less than <
equal or less than <=
greater than >
equal or greater than >=
equal ==
not equal !=
shift left <<
shift right >>

Binary arithmetic operators

plus +
minus -
multiply *
divide /
modulo %

Unary arithmetic operators

positive +
negative -
complement ~

Built-in functions

valueof(event)

An event can have a value. This function returns the value of the specified event.

myVar = valueof(myEvent)

as

Casts a variable. The following example casts a literal from integer to real.

myReal = 12 as real

active(state)

Returns true if the specified state is active and false otherwise.

myBool = active(StateA)

Complete statechart language grammar

This section presents the complete grammar of the statechart language. If you are in doubt how to write down a transition specification, a variable declaration, or any other syntactical construct of the YAKINDU statechart language, this is your definite reference that clarifies all syntactical questions.

The grammar is visualized using railroad diagrams. Railroad diagrams are easy to read and comprise all syntactically valid language constructs. Figure "Railroad diagrams explained" shows an example:

Railroad diagrams explained

Railroad diagrams explained

On the left-hand side you’ll find the so-called non-terminal symbol (or non-terminal for short) that is being defined by this particular diagram. Here the non-terminal is SimpleElementReferenceExpression. To find out what a SimpleElementReferenceExpression is allowed to look like, follow the „railroad track” just like a train would do. At each junction, the train either keeps its direction or changes it by 45 degrees, but it cannot make sharp turns of 90 degrees or more.

On its way, the train passes several „stations”. Each station represents a language element that is valid at this point. In our example, the first station is a rectangle with a grey background. The grey background denotes a non-terminal, and the word inside the rectangle is the non-terminal’s name, here: ID. You will have to look up this particular non-terminal’s railroad diagram to learn more about it. However, for simplicity and because their meanings are more or less obvious, a few non-terminals are not refined further. Examples are STRING, INT, BOOL, or ID.

In a SimpleElementReferenceExpression, a valid ID – a name – is always the first element, e.g. init42. If the train turns right at the junction, it moves directly to the railroad diagram’s exit. That means an ID like init42 is a valid SimpleElementReferenceExpression. Please note that when the train reaches the junction right before the exit, it cannot turn left, because trains don’t move that way. That is, the train is unable to travel to the ( station.

The train can reach the ) from the opposite direction only. After having left the initial ID station, the train can move to the left at the junction and then run a stretch touching only the ( and ) stations, in that order. These rectangles have a white background color, denoting a terminal symbol or terminal for short. A terminal stands for itself; write it down as it is.

Following that rule, init42() is a valid SimpleElementReferenceExpression. One or more Expressions inside the paranthesis, separated by the , terminal, are also valid. Example: init42(foo, bar + 27).

Please note:

The railroad diagrams themselves are generated from a textual grammar representation established and maintained with Xtext. If you suspect the diagrams shown here are lagging behind the actual implementation, look for files with the .xtext extension in the YAKINDU Statechart Tools source code distribution!

Please also note:

Not each and every construction that is syntactically allowed does make sense semantically. The statechart editor will flag such constructions as errors.

Statechart grammar

Figure "Statechart grammar" shows the statechart grammar. It makes use of the expressions grammar.

Statechart grammar

Statechart grammar

Expressions grammar

Figure "Expressions grammar" shows the expressions grammar. It is used by the statechart grammar.

Expressions grammar

Expressions grammar