A common concept for structuring statecharts is the decomposition into different sub state machines. This is a concept that you can find in modeling languages like the UML and others.
The basic idea is that a state machine which is assigned to a state as sub machine is executed as if it is part of the enclosing state machine. This means its life cycle is coupled to the life cycle of its owning state. It will be activated when its parent state becomes active, and will be deactivated when the parent state deactivates.
Such a sub machine pattern can be defined using the life cycle methods introduced in the previous chapter. The pattern for using sub state machines looks like this:
entry / otherMachine.enter oncycle / otherMachine.runCycle exit / otherMachine.exit
The call to
runCycle is only required if the referenced state machine is cycle-based. The equivalent is the following statement:
submachine statement is not only a shortcut. It is more expressive, less verbose and avoids errors by applying higher level validations. You can simply use this statement like any other within the state. Using it is recommended when you want to use sub state machines.
If the referenced state machine is event-driven, then the
triggerWithoutEvent operation can be called, which will perform a run-to-completion step without raising any event.
This allows the guard conditions to be evaluated and the transitions to potentially fire. It is also suitable for firing transitions with the “always” keyword.
So lets take a look how sub state machines can be applied to the traffic control example. The state Operate contains several sub states which control the traffic flow by alternately releasing and blocking the traffic flow of the two directions.
This set of sub states consists of two groups which are similar to each other. The first group consists of the states all blocked before A, Release A, and A Released. The second group is identical except for the fact that A is replaced by B. So the behavior is cloned. Sub state machines can be used to eliminate such redundancies.
Here, introducing the submachine required three steps.
var trafficLight : TrafficLight
var process : ReleaseProcess
entry / process.trafficLight=trafficLightA
This results in the following TwoWayTrafficController and ReleaseProcess state machines:
This example shows a typical scenario where using sub machines makes much sense. Here, sub machine controls a process which consists of several steps. Here, it just contains a happy case but it may also define error states. This process can be reused easily if the state machine should be extended to a 3 or 4-way traffic control. So setting up a 4-way traffic control in the initial statechart would require 4x3=12 states while using a sub machine requires 4+3=7 states.
In this example the state machine
process is used as sub machine in both states
Process A and
Proces B. When state machine
process reaches its final state, the state transition from state
Process A to state
Process B will be executed. During this state transition, first,
Process A will be exited. As a result also the sub machine will be exited. Second,
Process B will be entered. As a result, the
process state machine will be configured to use the other traffic light state machine and the
process state machine will be activated by calling its
enter operation. This will activate the initial state
Safe. So the
process state machine is reset to control the next process.
Sub machines are comparable with sub regions and as it is possible to define multiple sub regions for a state it is also possible to define any number of sub state machines. It is even possible that a state defines both – sub regions and sub machines. If both are defined then they are processed according to the execution order:
Additionally, the order of the
submachine statement within a state’s behavior definition is relevant for the execution order:
submachinestatement are executed before entering the sub machine. Those defined after are executed later. This may be relevant for properly setting up sub machines.
submachinestatement are executed before exiting the sub machine. Those defined after are executed later. This may be relevant for properly tearing down sub machines.
submachinestatement are executed before executing the sub machine’s run-to-completion step. Those defined after are executed later.
As shown in the example a state machine can be used as a sub machine within different states. The only constraint that must be considered is that these states must be exclusive to each other so that both states can’t be active at the same time. In other words, a machine can only be a sub machine in one active state at once. The subsequent use of a state machine as sub machine is no problem.