h1(#YAKINDUStatechartToolsReference). YAKINDU Statechart Tools Reference h2. Statechart elements This chapter describes the state chart elements of the YAKINDU SCT 2 editor. The meta model of the YAKINDU SCT 2 is the model of finite state machines. It is based on the view of a system that is defined by a finite number of states. The behavior of such a system is based on its active states. These states are determined by the history of the state machine. Very important are the theoretical models for state machines by Mealy and Moore. Mealy state machines associate actions with transitions. Moore machines associate actions with states (entry, exit). YAKINDU SCT 2 can model both these types. The YAKINDU SCT 2 meta model is designed similar to the UML statechart meta model with the following differences: * SCT statecharts are self-contained with interfaces defined by events and variables. * Core execution semantics are cycle-driven, not event-driven. ** This allows to process concurrent events. ** Event-driven behavior can be defined on top. * Time is an abstract concept for statecharts. * Time control is delegated to the environment. The model interpreter and different flavors of generated code are following these same core semantics. Please refer to the Wikipedia article "UML state machine":http://en.wikipedia.org/wiki/UML_state_machine for more details. h3(#Regions). Regions As already mentioned, the YAKINDU statecharts are self-contained. They are organized in regions. Due to this it is possible to organize multiple state machines in different regions and to run them concurrently. !images/parallelRegions.jpg(Parallel regions)! h3(#States). 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 inside this region. During simulation, each state can be active or passive. An active state has actions that are accomplished. Either an action is carried out on entering a state, during active state, or on exit. h3(#Transitions). Transitions A transition is the transfer of one state to another. Transitions are diagrammed as arrows. They can have events and actions, but don't need to. The syntax of events and actions is defined by a "textual description language":#Statechartdescriptionlanguage. Please refer to section "Events":#Events of this documentation for more details! For more details on actions please see chapter "Actions":#ReactionTriggers. If a state has more than a single outgoing transition without event, then among those transitions the one that has been modeled first will be carried out. h3(#Initialstateandfinalstate). Initial state and final state Initial and final states are pseudo states, because the statechart does not rely on them. Pseudo states express characteristics that are impossible to express by simple states. The initial state is always the first state that becomes active during interpretation or simulation of the state machine. An initial state can have only one outgoing transition and has no incoming ones. Its outgoing transition has no events or actions. Within a region, only a single initial state is allowed, but each region may have its own initial state. h3(#Choice). Choice A choice is also 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. h3(#Junction). Junction A junction is a pseudo state to combine transitions. This is very convenient if a state machine has many similar transitions. Junctions add clear arrangements to the state machine. h3(#CompositeState). Composite states A composite state is a state comprising one or more state machines. These state machines are also organized in regions within the composite state. Besides the simple composite state YAKINDU SCT knows about two kinds of composite states: orthogonal states and submachine states. Composite states contain other state machine branches. h4(#Orthogonalstates). Orthogonal states In the context of state machines orthogonal states are states that are independent of each other. The presumably most famous example is the keyboard example: !images/orthogonalState_example.jpg(Orthogonal states)! h4(#Submachinestates). Subdiagrams When using composite states, a statechart model often becomes too big to give a comprehensive overview of the whole diagram. Although it is possible to collapse and expand a state's figure compartment, these actions would spoil the diagram layout each time they are executed. Subdiagrams come as a solution. !images/extract_subdiagram.png(Composite state)! When the _Extract Subdiagram_ refactoring is executed on a composite state, all containing regions are extracted into a separate diagram. A small decorator in the lower-right corner of the state indicates the existence of such a subdiagram. When you hover with the mouse cursor over this decorator, you'll see a small preview of the subdiagram's content. The refactoring also creates the required entry and exit points for you. !images/extract_subdiagram2.png(Subdiagram popup window)! A click on the decorator opens the subdiagram in a separate editor tab. The breadcrumb at the top allows easy navigation throughout the hierachy levels. !images/extract_subdiagram3.png(Subdiagram editor)! h3(#ShallowHistory). Shallow history The shallow history state is a pseudo state that is placed inside regions of composite states. 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. The following example, showing the answering of a questionaire, explains this: !images/shallowHistory01.jpg(Shallow history [1])! Particularly interesting in this statechart are the events _checkProgress_ and _goon_. The event _checkProgress_ 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. !images/shallowHistory02.jpg(Shallow history [2])! !images/shallowHistory03.jpg(Shallow history [3])! When the _goon_ event is triggered, the most recent active state inside of the composite state *answeringQuestions* is activated again. h3(#DeepHistory). Deep history Deep history is similar to shallow history, but more complex. With a deep history the latest state of multiple nested states is remembered. h2(#Statechartdescriptionlanguage). Statechart description language A textual description language is used to declare and describe behaviors in a state machine. It is case sensitive. h3(#Typesystem). Type system The language has a small integrated type system consisting of the following simple types: * integer * real * boolean * string * void Events and variables can be declared using types: bc(prettyprint).. var intVar : integer var realVar : real var boolVar : boolean var stringVar : string event addInt : integer event addReal : real event checkValidity : boolean event stringEvent : string event voidEvent : void h3(#Expressions). Expressions Expressions in SCT 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*. h4(#LogicalAND). Logical AND bc(prettyprint).. var1 && var2 h4(#LogicalOR). Logical OR bc(prettyprint).. var1 || var2 h4(#LogicalNOT). Logical NOT bc(prettyprint).. !var1 h4(#Conditionalexpression). Conditional expression bc(prettyprint).. var1 ? var2 : 23 h4(#BitwiseXOR). Bitwise XOR bc(prettyprint).. var1 ^ var2 h4(#BitwiseOR). Bitwise OR bc(prettyprint).. var1 | var2 h4(#BitwiseAND). Bitwise AND bc(prettyprint).. var1 & var2 h4(#LogicalRelationsandShiftOperators). Logical Relations and Shift Operators |less than | @<@ | |equal or less than | @<=@ | |greater than | @>@ | |equal or greater than | @>=@ | |equal | @==@ | |not equal | @!=@ | |shift left | @<<@ | |shift right | @>>@ | h4(#Binaryarithmeticoperators). Binary arithmetic operators |plus | @+@ | |minus | @-@ | |multiply | @*@ | |divide | @/@ | |modulo | @%@ | h4(#Unaryarithmeticoperators). Unary arithmetic operators |positive | @+@ | |negative | @-@ | |complement | @~@ | h3(#Statements). Statements A statement is one of three kinds: * assignment * event raising * operation call 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 | |= |
====
An event is raised by calling a method whose name consists of the word _raise_ followed by the event name, e. g. _raiseIncoming_call()_, and if it is an interface event the name of the interface.
An operation is called similar to other programming languages with the operation name and passing concrete parameters. The parameters can be expressions.
h3(#Scopes). Scopes
====
h4(#Namespace). Namespace
The language allows to define unique namespaces which can be used to qualify references to the statechart.
bc(prettyprint)..
namespace trafficlights
p. ====
====
h4(#interfacescope). Interface scope
Declarations in the interface scope are externally visible. They can be shared within the environment.
bc(prettyprint)..
interface NamedInterface:
in event event1
out event event3 : integer
var variable1 : integer
p. ====
====
h4('internalscope). Internal scope
Declarations made in an internal scope are visible to contained states only.
bc(prettyprint)..
internal:
var localVariable1: integer
event localEvent: integer
local event localEvent2
operation localOperation (int1 : integer, int2 : integer): integer
localEvent2 / raise NamedInterface.event3 :
localOperation(valueof(localEvent) , NamedInterface.variable1)
p. ====
h3(#Declarations). Declarations
Within scopes events, variables, operations, and local reactions can be declared.
====
h3(#Events). Events
An event in an interface scope has a direction. It is either ingoing or outgoing:
bc(prettyprint)..
interface NamedInterface:
in event event1
out event event2
p. An event in the local scope can carry variables:
bc(prettyprint)..
internal:
event localEvent1 : integer
p. A local event can have a value assignment:
bc(prettyprint)..
internal:
event localEvent1: integer = 25
p. ====
====
h3(#Variables). Variables
Variables can have different visibilities. They can be visible for the environment:
bc(prettyprint)..
var variable1: real
p. Variables can be *readonly* (constants):
bc(prettyprint)..
var readonly pi: real = 3.1415
p. Variables can be referenced by the environment.
bc(prettyprint)..
var external variable3: integer = 34
p. ====
====
h3(#Constants). Constants
Variables can be immutable. For this special variable the keyword @const@ is used:
bc(prettyprint)..
const variable1: real
p. ====
h3(#ReactionTriggers). Reaction Triggers
Actions are key constructs in state machines to model behavior. YAKINDU SCT 2 knows about the following kinds of actions.
====
h4(#after). after
The _after_ trigger specifies a one-shot time event.
After the specified 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 time starts when the state or statechart is entered.
bc(prettyprint). after 20 s
Structure:
@after@ _@time@_ _@unit@_
The time value may be a literal or an expression that returns an integer value.
The time unit can be:
* ==== s - seconds ====
* ==== ms - milliseconds ====
* ==== us - microseconds ====
* ==== ns - nanoseconds ====
p. ====
====
h4(#every). 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.
bc(prettyprint). every 200 ms
Structure:
@every@ _@time@_ _@unit@_
The time value may be a literal or an expression that returns an integer value.
The time unit can be:
* s - seconds
* ms - milliseconds
* us - microseconds
* ns - nanoseconds
====
====
h4(#always). always
This trigger is always true and enables a reaction to be executed in every run-to-completion step (RTC). It is equivalent to _oncycle_.
====
====
====
h4(#defaultelse). default, else
The _default_ trigger is equivalent to the _else_ trigger. It 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. The _default_ trigger can only be be used in transitions and implies the lowest evaluation priority for that transition.
====
====
====
h4(#entry). entry
An _entry_ trigger marks actions that are carried out on entering a state or state machine.
====
====
h4(#exit). exit
An _exit_ trigger marks actions that are carried out on exiting a state or state machine.
====
====
h4(#oncycle). oncycle
The _oncycle_ trigger is always true and enables a reaction to be executed in every run-to-completion step (RTC). It is equivalent to _always_.
====
====
h3(#Operations). Operations
Operations can have none, one or multiple parameters. A parameter is declared with a name and a type. An operation may have a single return type similar to Java.
bc(prettyprint)..
operation localOperation (xValue : integer, yValue : integer):integer
p.
====
h3(#BuildInFunctions). Build-in functions
====
h4(#valueofevent). valueof(event)
====
Returns the value of an valued event that it passed to the function as parameter.
bc(prettyprint)..
myVar = valueof(myEvent)
p.
====
====
h4(#as). as
Casts a variable. The following example casts a literal from integer to real.
bc(prettyprint)..
myReal = 12 as real
p.
====
====
h4(#activestate). active(state)
Returns _true_ if a state is active and _false_ otherwise.
bc(prettyprint)..
myBool = active(StateA)
p.
====
h3(#LocalReactions). Local reactions
Local reactions describe the internal behavior of a state. So they have internal scope. A local reaction is declared as follows:
bc(prettyprint)..
LocalReaction: ReactionTrigger '/' ReactionEffect ('#' ReactionProperties)?
ReactionTrigger: (Event ("," Event )* (=> '[' Expression ']')?) | '[' Expression ']'
ReactionEffect: Statement (';' Statement )* (';')?
Statement: Assignment | EventRaising | OperationCall
ReactionProperties: (EntryPoint | ExitPoint)*
p. Within a local reaction an interface event can be raised:
bc(prettyprint)..
internal:
localEvent1 / raise NamedInterface.event3 : localOperation (valueof(localEvent), NamedInterface.variable1);
p. A local reaction can have a priority value. The latter is defined by appending the character @#@ and the numeric priority value to the local reaction's definition. Examples:
====
bc(prettyprint#GeneratorFeatures)..
localEvent2 / NamedInterface.variable2 += 3; #1
localEvent3 / NamedInterface.variable4 += 2.0; #2
h2(#Codegeneration). SGen generator features
All generators can be customized by a generator model. This is a textual model in a file, specifying generator features, i.e. the outlet path. The following screenshot shows an sample configuration for the Java code generator.
To get started with the generator model, YAKINDU Statechart Tools includes a wizard that creates a basic configuration file with default values.
!images/sGenEditor.png(SGen generator model with default values)!
====
The generator model is associated with the builder. If _Project → Build Automatically_ is checked, the generator automatically creates its output files for each modification the user makes to the statechart model. Below the specific customizing features of the generator models are explained.
The following section describes the *Core Features* which are available for all code generators:
====
h4(#Outlet). Outlet
The *Outlet* feature specifies target project and target folder for the generated artifacts. It is a _required_ feature and has the following parameters:
# __targetProject__ (String, required): The project to store the generated artifacts to.
# __targetFolder__ (String, required): The folder to store the generated artifacts to. If a library folder is given, only the dynamic (i.e. model dependent artifacts) are generated into the target folder, if not all generated artifacts will be generated into it. All artifacts in this folder will be overwritten during re-generation.
# __libraryTargetFolder__ (String, optional): The folder to store the static (i.e. model independent artifacts) to. In case this is not specified, all artifacts will be generated into the target folder. All artifacts in this folder will be preserved during re-generation.
Sample configuration:
bc(prettyprint)..
feature Outlet {
targetProject = "SampleProject"
targetFolder = "src-gen"
libraryTargetFolder = "src"
}
p. ====
====
h4(#LicenseHeader). LicenseHeader
The *LicenseHeader* feature specifies the license text that is to be added as a header to the generated artifacts. It is an *optional* feature and has the following parameters:
# __licenseText__ (String, required): License text to be added as a file header
Sample configuration:
bc(prettyprint)..
feature LicenseHeader {
licenseText = "Copyright (c) 2012 committers of YAKINDU and others."
}
p. ====
====
h4(#FunctionInlining). FunctionInlining
The *FunctionInlining* feature enables the inlining of expressions instead of generating separate functions or methods. This might reduce the readability of the generated code, but increases performance, because less operation calls are necessary.
It is an *optional* feature and has the following parameters:
# __inlineReactions__ (Boolean, optional): inlines the expression for reactions
# __inlineEntryActions__ (Boolean, optional): inlines the expression for entry actions
# __inlineExitActions__ (Boolean, optional): inlines the expression for exit actions
# __inlineEnterSequences__ (Boolean, optional): inlines the expression for enter sequences
# __inlineExitSequences__ (Boolean, optional): inlines the expression for exit sequences
# __inlineChoices__ (Boolean, optional): inlines the expression for choices
# __inlineEnterRegion__ (Boolean, optional): inlines the expression for enter regions
# __inlineExitRegion__ (Boolean, optional): inlines the expression for exit regions
# __inlineEntries__ (Boolean, optional): inlines the expression for entries
Sample configuration:
bc(prettyprint)..
feature FunctionInlining {
inlineChoices = false
inlineEnterRegion = true
inlineEntries = true
}
p. ====
====
h4(#Debug). Debug
The *Debug* feature dumps the execution model to the target folder as an XMI model. It is an *optional* feature and has the following parameter:
# __dumpSexec__ (Boolean, required): dumps the execution model as XMI model
Sample configuration:
bc(prettyprint)..
feature Debug {
dumpSexec = true
}
p. ====
h2(#JavaGeneratorFeatures). Java generator features
====
h4. Naming feature
The *Naming* feature allows the configuration of package names as well as class name prefix / suffix.
It is an *optional* feature and has the following parameters:
# __basePackage__ (Boolean, required): The package to create for the generated java classes
# __implementationSuffix__ (Boolean, optional): The suffix for the implementing classes
Sample configuration:
bc(prettyprint)..
feature Naming {
basePackage = "org.yakindu.sct"
implementationSuffix = "Impl"
}
p. ====
====
h4. RunnableWrapper
Generates a runnable wrapper for the state machine. This feature generates an additional Java class providing a thread-safe wrapper for the generated state machine implementation. In addition to the specific state machine interface it implements the __Runnable__ interface and can be executed in a thread. It implements an event queue and event-driven execution semantics. As the wrapper adds thread safety any number of client threads may call the state machine.
It is an *optional* feature and has the following parameters:
# __namePrefix__ (String, optional): prefix of the implementing class' name
# __nameSuffix__ (String, optional): suffix of the implementing class' name
Sample configuration:
====
bc(prettyprint)..
feature RunnableWrapper {
namePrefix = "org.yakindu.sct"
nameSuffix = "Impl"
}
p. ====
====
h4(#GeneralFeatures). GeneralFeatures
The *GeneralFeatures* feature allows to configure additional services to be generated along with the state machine. Per default, all parameters are __false__. It is an *optional* feature and has the following parameters:
====
# __InterfaceObserverSupport__ (Boolean, optional): enables/disables the generation of listener interfaces for the state machine
# __RuntimeService__ (Boolean, optional): enables/disables the generation of a runtime service that triggers the run cycle of a cycle-based state machine
# __TimerService__ (Boolean, optional): enables/disables the generation of a timer service implementation using __java.util.Timer__
====
====
Sample configuration:
bc(prettyprint)..
feature GeneralFeatures {
InterfaceObserverSupport = true
RuntimeService = true
TimerService = true
}
p. ====
h2(#CGeneratorFeatures). C/C++ generator features
====
h4. IdentifierSettings
The *IdentifierSettings* feature allows the configuration of module names and identifier character length:
# __moduleName__ (String, optional): name for header and implementation, default: statechart name
# __statemachinePrefix__ (Boolean, optional): prefix which is prepended to function, state, and type names.
# __maxIdentifierLength__ (Integer, optional): maximum number of characters of an identifier, default: 31 characters, which is complying with the ANSI C99 standard.
# __separator__ (String, optional): character to replace whitespace and otherwise illegal characters in manes.
Sample configuration:
bc(prettyprint)..
feature IdentifierSettings {
moduleName = "MyStatechart"
statemachinePrefix = "myStatechart"
maxIdentifierLength = 31
separator = "_"
}
p. ====
====
h4. GeneratorOptions
The *GeneratorOptions* feature allows change the behavior of the C++ generator:
# __innerFunctionVisibility__ (String, optional): This parameter is used to change the visibility of inner functions and variables. By default @private@ visibility is used. It can be changed to @protected@ to allow function overriding for a class which inherits from the generated state machine base class.
# __staticOperationCallback__ (Boolean, optional): If this parameter is set to _true_, the callback function declaration for statechart operations is static and the functions are called statically by the state machine code.
Sample configuration:
bc(prettyprint)..
feature GeneratorOptions {
innerFunctionVisibility = "protected"
staticOperationCallback = true
}
p. ====
====
h4. JUnitWrapper
Using the *JUnitWrapper* feature it is possible to create JUnit tests that will run the generated gtests.
# __WrapToJUnit__ (Boolean): This parameter determines whether a JUnit wrapper test is to be generated __(true)__ or not __(false)__.
Sample configuration:
bc(prettyprint)..
feature JUnitWrapper {
WrapToJUnit = "false"
}
p. ====
h2(#CreatingCustomCodeGenerators). Creating custom code generators
YAKINDU Statechart Tools provide a rich feature set to support custom code generators out of the box. These code generators can be either written in Java, "Xtend":http://www.eclipse.org/xtend/, or "Xpand":http://www.eclipse.org/modeling/m2t/?project=xpand.
h3(#WritingacustomcodegeneratorwithXtend2Java). Writing a custom code generator with Xtend2/Java
First, you have to create a new Xtend2 generator project. Click __File → New → Other... → YAKINDU → YAKINDU Xtend2/Java Generator Project__ to create a new Xtend2 generator project.
!images/xtendGenerator.png(Creating an Xtend2 generator project)!
The wizard asks for a *Project name* and the name of the *Generator class*, which has to be the fully-qualified class name. If you check the *Use Xtend* checkbox, the generator class will initially be created as an "Xtend":http://www.eclipse.org/xtend/ class. Otherwise, Java will be used for the generator.
The check box *Configure for Plugin Export* adds all required extension point registrations to the new project for exporting it as a plugin. The generator model can refer to the new generator plugin via its unique *Generator ID*. If you want to contribute custom generator features for your code generator, check the *Create Feature Library* check box.
After clicking on *Finish* a new project is created in your workspace. All required plugin dependencies and extension points are registered and you can start to write your code generator based on the ExecutionFlow meta model [Link].
h3(#ExecutingacustomXtend2Javacodegenerator). Executing a custom Xtend2/Java code generator
YAKINDU Statechart Tools provide a convenient way to execute your generator while you are developing it.
For this, you have to create a new *generator model* with the generator ID *yakindu::generic*, either by using the *New Statechart Generator Model* wizard or by simply creating a new text file with the file extension @.sgen@. The feature described below allows to configure your code generator.
====
h4(#Generator). Generator
The *Generator* feature allows to configure a custom code generator located in the workspace and written in Java or in another JVM language. It is a *required* feature and has the following parameters:
# __generatorProject__ (String, required): the name of the generator project
# __generatorClass__ (String, required): the fully-qualified name of the code generator class
# __configurationModule__ (String, optional): the fully-qualified class name of a Guice module to configure the code generator
Sample configuration:
bc(prettyprint)..
feature Generator {
generatorProject = "org.yakindu.sct.mygenerator"
generatorClass = "org.yakindu.sct.MyGenerator"
}
p. ====
h3(#ExecutingacustomXpandcodegenerator). Executing a custom Xpand code generator
In order to execute an Xpand-based custom code generator, you have to create a new *Generator Model* with the generator ID *yakindu::xpand*, either by using the *New Statechart Generator Model* wizard or by simply creating a new text file with the file extension *.sgen*. The following feature allows to configure your code generator.
====
h4(#Template). Template
The *Generator* feature allows to configure a custom code generator located in the workspace and written in Java or in another JVM language. It is a *required* feature and has the following parameters:
# __templateProject__ (String, required): the name of the generator project
# __templatePath__ (String, required): the fully-qualified template path of the main template
Sample configuration:
bc(prettyprint)..
feature Template {
templateProject = "ExampleProject"
templatePath = "org::yakindu::sct::generator::xpand::Main::main"
}
p. ====
h2(#APISpecification). API specifications of the generated code
The explanations below are using the _TrafficLight_ sample state machine to describe the API specifications of the code generated by the YAKINDU C and Java code generators. The image below is showing the statechart. It models a pedestrian crossing with push-button operated traffic lights ("pelican crossing").
!(img-rounded shadowed#SpecJava)images/TrafficLight.png(The traffic light model)!
h3(#JavaSpec). Specifications of Java code
You can checkout the Java sample project _"org.yakindusct.examples.trafficlight":https://github.com/Yakindu/statecharts/tree/master/examples/org.yakindu.sct.examples.trafficlight_ from the "YAKINDU Statechart Tools GitHub repository":https://github.com/Yakindu/statecharts. The Java example contains statechart, SGen model, graphical widgets, and some glue code to connect the generated code with the widgets. The graphical widgets are based on SWT.
To execute the Java example, run the file _CrossingDemoCycleBased.java_ as "Java Application" from the Eclipse _Run As_ context menu.
h4(#JavaGeneratedCode). Generated code files
You will find the generated code in the _src-gen_ folder of the traffic light example.
The package _org.yakindu.sct.examples.trafficlight.cyclebased_ contains the general state machine interfaces and classes. They are needed by each particular state machine and are independend from concrete ones.
h4. Interface IStatemachine
Each generated state machine implements the interface @IStatemachine@:
bc(prettyprint)..
package org.yakindu.sct.examples.trafficlight.cyclebased;
/**
* Basic interface for state machines.
*
*
*/
public interface IStatemachine {
/**
* Initializes the state machine. Use to init internal variables etc.
*/
public void init();
/**
* Enters the state machine. Sets the state machine in a defined state.
*/
public void enter();
/**
* Exits the state machine. Leaves the state machine with a defined state.
*/
public void exit();
/**
* Start a run-to-completion cycle.
*/
public void runCycle();
}
h5. Fundamental statechart methods
The generated code contains fundamental methods to initialize, enter, and exit a state machine, as well as a method to start a run-to-completion step.
The @IStatemachine@ interface specifies the four methods @init()@, @enter()@, @exit()@ and @runCycle()@.
* The @init()@ method is used to initialize the internal objects of the state machine right after its instantiation. Variables are initialized to their respective default values. If the statechart defines initialized variables, these initializations are also done in the @init()@ method.
* The @enter()@ method must be called to enter the state machine. It brings the state machine to a well-defined state.
* The @exit()@ method is used to leave a state machine statefully. If for example a history state is used in one of the top regions, the last active state is stored and the state machine is left via @exit()@. Re-entering it via @enter()@ continues to work with the saved state.
* The @runCycle()@ method is used to trigger a run-to-completion step in which the state machine evaluates arising events and computes possible state changes. Somewhat simplified, a run-to-completion cycle consists of the following steps:
# Clear list of outgoing events.
# Check whether events have occurred which are leading to a state change.
# If a state change has to be done:
## Execute exit actions of the present state.
## Save history state, if necessary.
## Make the new state active. ====
## Execute entry actions of the new state.
# Clear list of incoming events.
====
====
====
h4. Timed state machines
If a statechart uses timing functionality, additional classes are generated.
The traffic light example uses timing funtionality, namely _after_ clauses. To support time-controlled behavior, the interfaces @ITimedStatemachine@, @ITimerService@ and the class @TimeEvent@ are generated.
The @ITimedStatemachine@ interface extends the generated state machine by a @ITimerService@ attribute. The client code must provide an implementation of that interface. It may use the @TimerService@, an @ITimerService@ implementation that is also generated.
@ITimedStatemachine@ also specifies the callback method @onTimeEventRaised(TimeEvent timeEvent)@, enabling the timer service to raise time events.
bc(prettyprint)..
package org.yakindu.sct.examples.trafficlight.cyclebased;
/**
* Interface for state machines which use timed event triggers.
*/
public interface ITimedStatemachine {
/**
* Set the {@link ITimerService} for the state machine. It must be set
* externally on a timed state machine before a run cycle can be correct
* executed.
*
* @param timerService
*/
public void setTimerService(ITimerService timerService);
/**
* Returns the currently used timer service.
*
* @return {@link ITimerService}
*/
public ITimerService getTimerService();
/**
* Callback method if a {@link TimeEvent} occurred.
*
* @param timeEvent
*/
public void onTimeEventRaised(TimeEvent timeEvent);
}
p. @ITimedStatemachine@ extends the generated state machine by a @ITimerService@ that can be set, and provides a callback method @onTimeEventRaised(TimeEvent timeEvent)@, enabling the timer service to raise @TimeEvents@.
p. Basically the proper time handling has to be implemented by the developer, because timer functions generally depend on the hardware target used. So for each hardware target a timer service class implementing the @ITimerService@ interface has to be developed. Let's have a look at the @ITimerService@ interface:
bc(prettyprint)..
package org.yakindu.sct.examples.trafficlight.cyclebased;
/**
* Interface a timer service has to implement. Use to implement your own timer
* service. A timer service has to be added to a timed state machine.
*
*/
public interface ITimerService {
/**
* Starts the timing for a given {@link TimeEvent}.
*
* @param event
* : The TimeEvent the timer service should throw if timed out.
* @param time
* : Time in milliseconds after the given time event should be
* triggered
* @param cycleStartTime
* : The absolute start time in milliseconds at which the last
* run cycle was called. Can be used to produce a more accurate
* timing behavior.
*/
public void setTimer(TimeEvent event, long time, long cycleStartTime);
/**
* Unset the given {@link TimeEvent}. Use to unset cyclic repeated time
* events.
*
* @param event
*/
public void resetTimer(TimeEvent event);
/**
* Cancel timer service. Use this to end possible timing threads and free
* memory resources.
*/
public void cancel();
/**
* Returns the system time in milliseconds.
*
* @return the difference, measured in milliseconds, between the current
* time and a defined point of time in the past.
*/
public long getSystemTimeMillis();
}
p. The @ITimerService@ interface defines the following methods dealing with timing functionality:
h5. Method setTimer
p. A state machine calls the @public void setTimer(TimeEvent event, long time, long cycleStartTime)@ method to tell the timer service that it has to start a timer for the given time event and raise it after the period of time specified by the _time_ parameter has expired. It is important to only start a timer thread or a hardware timer interrupt within the @setTimer()@ method and avoid any time-consuming operations like extensive computations, @Thread.sleep(…)@ or waiting. Otherwise the state machine execution might hang within the timer service or might not show the expected runtime behavior.
The parameter @cycleStartTime@ holds the absolute start time of the last run cycle. It can be used to reduce or remove the time offset used by the state machine's runtime operations in order to achieve a more precise timing behavior.
h5. Method resetTimer
p. The state machine calls the method @resetTimer(TimeEvent event)@ to notify the timer service to reset the timer for the given event.
h5. Method onTimeEventRaised
p. In order to notify the state machine about the occurence of a @TimeEvent@ after a period of time has expired, the method @onTimeEventRaised(TimeEvent timeEvent)@ is called on the state machine. For this purpose, the state machine must implement the @ITimedStatemachine@ interface.
The time event is recognized by the state machine and will be processed during the next run cycle.
You can conclude that in order to process the time events raised by the timing service without too much latency, the runtime environment has to call the state machine's @runCycle@ method as frequently as needed. Consider for example a time event which is raised by the timer service after 500 ms. However, if the runtime environment calls the state machine's @runCycle()@ method with a frequency of once per 1000 ms only, the event will quite likely not be processed at the correct points in time.
h5. Class TimeEvent
p. An important class used by timed state machines is @TimeEvent@:
bc(prettyprint)..
package org.yakindu.sct.examples.trafficlight.cyclebased;
/**
* Event that reflects a time event. It's internally used by
* {@link ITimedStatemachine}.
*
* @author muehlbrandt
*
* @param Iface
_interface_name_@_@[ set
| get
| raise
]@_@[ _variable_name_ | _event_name_ ]. For example, the getter of the _red_ variable of the _pedestrian_ interface is named @trafficLightWaitingIfacePedestrian_get_red(TrafficLightWaiting* handle)@.
h4. Timed state machines
If a statechart uses timing functionality or external operations, an additional header file is generated. Its name matches the pattern _statechart_name_@Required.h@. This header file defines method hooks the client code has to implement externally.
The traffic light example uses timing funtionality, namely _after_ clauses. To support time-controlled behavior, such an additional header file is generated.
bc(prettyprint)..
extern "C" {
#endif
/*! \file This header defines prototypes for all functions that are required by the state machine implementation.
This is a state machine uses time events which require access to a timing service. Thus the function prototypes:
- trafficLightWaiting_setTimer and
- trafficLightWaiting_unsetTimer
are defined.
These functions will be called during a 'run-to-completion step' (runCycle) of the statechart.
There are some constraints that have to be considered for the implementation of these functions:
- never call the statechart API functions from within these functions.
- make sure that the execution time is as short as possible.
*/
//
// This is a timed state machine that requires timer services
//
//! This function has to set up timers for the time events that are required by the state machine.
/*!
This function will be called for each time event that is relevant for a state when a state will be entered.
\param evid An unique identifier of the event.
\time_ms The time in milli seconds
\periodic Indicates the the time event must be raised periodically until the timer is unset
*/
extern void trafficLightWaiting_setTimer(const sc_eventid evid, const sc_integer time_ms, const sc_boolean periodic);
//! This function has to unset timers for the time events that are required by the state machine.
/*!
This function will be called for each time event taht is relevant for a state when a state will be left.
\param evid An unique identifier of the event.
*/
extern void trafficLightWaiting_unsetTimer(const sc_eventid evid);
#ifdef __cplusplus
}
#endif
#endif /* TRAFFICLIGHTWAITINGREQUIRED_H_ */
p. Basically the proper time handling has to be implemented by the developer, because timer functions generally depend on the hardware target used. So for each hardware target the client code must provide a method to set a timer and another method to reset it. The methods have to be implemented externally and linked to the generated code.
p. The following methods are dealing with timing functionality:
h5. Method setTimer
p. A state machine calls the @setTimer()@ method – short for the method's full name like e. g. @trafficLightWaiting_setTimer(const sc_eventid evid, const sc_integer time_ms, const sc_boolean periodic)@ – to tell the timer service that it has to start a timer for the given time event identifier and raise it after the period of time specified by the _time_ms_ parameter has expired. It is important to only start a timer thread or a hardware timer interrupt within the @setTimer()@ method and avoid any time-consuming operations like extensive computations, sleeping or waiting. Never call the statechart API functions from within these functions! Otherwise the state machine execution might hang within the timer service or might not show the expected runtime behavior.
In order to have the timer service raise the time event periodically, the parameter _periodic_ must be _true_.
h5. Method unsetTimer
p. The state machine calls the method @trafficLightWaiting_unsetTimer(const sc_eventid evid)@ to notify the timer service to unset the timer for the given event ID.
h5. Method raiseTimeEvent
p. In order to notify the state machine about the occurence of a time event after a period of time has expired, the @raiseTimeEvent()@ method – defined in the header file of the state machine – is called on the state machine. In the case of the traffic light example it is named @trafficLightWaiting_raiseTimeEvent(TrafficLightWaiting* handle, sc_eventid evid)@ (in file _TrafficLightWaiting.h_).
The time event is recognized by the state machine and will be processed during the next run cycle.
You can conclude that in order to process the time events raised by the timing service without too much latency, the runtime environment has to call the state machine's @runCycle()@ method as frequently as needed. Consider for example a time event which is raised by the timer service after 500 ms. However, if the runtime environment calls the state machine's @runCycle()@ method with a frequency of once per 1000 ms only, the event will quite likely not be processed at the correct points in time.
h4(#COperationCallbacks). Operation callbacks
YAKINDU Statechart Tools support client code operations that can be used by a state machine and are executed as as actions. These operations have to be implemented in order to make a statechart executable. The figure below shows a sample statechart using an operation:
!images/OperationExample.png(Specifying an operation callback in the model)!
Let's have a look at the generated code:
bc(prettyprint)..
#ifndef DEFAULTREQUIRED_H_
#define DEFAULTREQUIRED_H_
#include "sc_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \file This header defines prototypes for all functions that are required by the state machine implementation.
This state machine makes use of operations declared in the state machines interface or internal scopes. Thus the function prototypes:
- defaultIface_myOperation
are defined.
These functions will be called during a 'run-to-completion step' (runCycle) of the statechart.
There are some constraints that have to be considered for the implementation of these functions:
- never call the statechart API functions from within these functions.
- make sure that the execution time is as short as possible.
*/
extern sc_integer defaultIface_myOperation(const sc_integer param1, const sc_boolean param2);
#ifdef __cplusplus
}
#endif
#endif /* DEFAULTREQUIRED_H_ */
p. An additional method @sc_integer defaultIface_myOperation(const sc_integer param1, const sc_boolean param2)@ has been generated. This method has to be implemented and linked with the generated code, so that the state machine can use it.
h4(#CIntegratingGeneratedCode). Integrating generated code
To get a clue how to integrate a generated C state machines with your project have a look at the @main.cpp@ file and its @main()@ method:
bc(prettyprint)..
#include "org_yakindu_sct_examples_c_trafficlight.h"
#include