Ultimately, you will need your state machine not only as a nice graphical statechart model. This chapter explains how to generate it as executable code.
You can integrate the generated code with your software project as a component. You can send events to the state machine, receive events from it, retrieve information regarding the currently active state (or states), set and get state machine variables, and have the state machine invoke specific behavior that is external to it.
itemis CREATE, comes with a couple of code generators that save you from the time-consuming and error-prone task of coding your state machine manually. The itemis CREATE code generators transform your statechart into source code of a target programming language in an instant. The generated code is always correct, at least in the sense that it is a true one-to-one mapping of your statechart – and at least unless a code generator is buggy itself.
Use SCTUnit in order to test whether your state machine is also correct in the sense that it does what it is intended to do.
Currently itemis CREATE support code generation for the following programming languages:
YSCT also includes a statechart image generator.
If you want to generate SCXML files, see also chapter SCXML code generation.
The generated state machine code implements one of two execution schemes:
You can select the execution scheme via the @CycleBased or @EventDriven annotations. Write the appropriate annotation to the top of your statechart’s definition section, see sections
"@CycleBased" and
"@EventDriven".
In the cycle-based execution scheme, each run cycle consists of two different phases:
This approach allows for explicitly processing several events at the same time as well as for checking combinations of events, e.g., a guard condition like
[eventA && eventB]. This is very useful for systems that are close to hardware and input signals. Basically it is the
input-process-output (IPO) model.
Example: The sample client code below first raises events eventA and eventB in the state machine. These events are lingering in the state machine until the client code calls runCycle() to actually process the events.
sc_raiseEventA(handle);
sc_raiseEventB(handle);
…
sc_runCycle(handle);
Please note: In the cycle-based execution scheme, an event that has been raised internally using the raise statement is visible in the run cycle “downstream” only, i.e., in such regions and the states therein that have not yet been processed in the current run cycle. Everything that is “upstream” in the run cycle cannot “see” this event. This is semantically different from the event-driven execution scheme. Read more on this topic in section "Raising and processing an event".
In the
event-driven execution scheme, each incoming event or time event immediately triggers the execution of a run-to-completion step. As a consequence, the state machine will never process two events simultaneously, and a guard condition like
[eventA && eventB] will never become true.
Example: The sample client code below raises the events eventA and eventB. The state machine processes each event immediately as it arrives, triggered by the respective raise…() method. Thus the client code does not need to call runCycle().
sc_raiseEventA(handle);
sc_raiseEventB(handle);
Please note: In the event-driven execution scheme, an event that is raised internally in a run-to-completion step using the raise statement will not be acted upon by any active state “downstream” in the event cycle. The reason is that only a single event can be processed at a time, and this is the event that caused the current run cycle to execute in the first place. The internally-raised event will trigger its own run-to-completion step subsequently. Thus it will be visible to all active states in that RTC. This is semantically different from the cycle-based execution scheme. Read more on this topic in section "Raising and processing an event".
The generated source code supports a basic statechart execution model that can be used to implement different variants. It is important to understand the responsibilities of the generated code, i.e., what it cares about and what it doesn’t. The remaining issues are out of scope and must be dealt with in the client code the developer has to provide.
The generated code basically takes care about the following:
There are some predefined constraints:
Out of scope are:
All these things are out of the generated code’s scope, since the code generators coming with itemis CREATE cannot provide tailored solutions without knowing any details about a respective specific environment, like runtime system, libraries, etc.
In order to have a better code generator support for specific usage patterns, you would have to implement corresponding code generator extensions.
In order to circumvent the missing thread-safety of the runCycle() method, the Java code generator has an option for generating a so-called runnable wrapper, providing event queueing and multi-threading support. For details, see section "GeneralFeatures". It is easy to implement this as a general solution for Java, since the programming language provides corresponding standard features that by definition are available everywhere and can simply be used.
The C programming language, however, does not have any standardized threading features. Therefore a general solution cannot be given. Specific solutions for specific threading libraries would require the implementation of a suitable code generator extension.
The following sections describe core features, which are available for all code generators. Individual code generators usually support specific additional features; please see the respective code generator documentation for details.
The Outlet feature specifies target project and target folder for the generated artifacts. It is a required feature.
Example:
feature Outlet {
targetProject = "SampleProject"
targetFolder = "src-gen"
libraryTargetFolder = "src"
skipLibraryFiles = "false"
}
Target Project
Target Folder
Library Target Folder
Skip Library Files
The OutEventAPI feature enables different generator features, which are used for out events.
Example:
feature OutEventAPI {
observables = true
getters = true
}
At least one of both options needs to be enabled.
Observables
Getters
The LicenseHeader feature specifies the license text to be added as a header to the generated artifacts. It is an optional feature.
Example:
feature LicenseHeader {
licenseText = "Copyright (c) 2017 committers of itemis CREATE and others."
}
License Text
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. This is an optional feature and all parameters of the FunctionInlining feature are false by default, except the Inline Choices parameter which is true by default.
Example:
feature FunctionInlining {
inlineChoices = false
inlineEnterRegion = true
inlineEntries = true
}
Inline Reactions
Inline Entry Actions
Inline Exit Actions
Inline Enter Sequences
Inline Exit Sequences
Inline Choices
Inline Enter Region
Inline Exit Region
Inline Entries
The Debug feature dumps the execution model to the target folder as an XMI model. It is an optional feature.
Example:
feature Debug {
dumpSexec = true
}
Dump Sexec
An SGen generator model may assign values to properties and later use these properties in expressions. The following sample generator model uses the var keyword to declare the properties projectName, version, isBeta, and generateTimerService with their respective types and default values. The model then uses these values in feature clauses by referring to the properties:
GeneratorModel for create::java {
var projectName : string = "light_switch"
var version : string = "1.0"
var isBeta : boolean = true
var generateTimerService : boolean = true
statechart myStateAutomaton {
feature Outlet {
targetProject = projectName
targetFolder = "src-gen/" + version + (isBeta ? "beta" : "")
libraryTargetFolder = "src"
}
feature GeneralFeatures {
TimerService = generateTimerService
}
}
}
The syntax rules for expressions in generator models are the same as for statechart language expressions, see section
"Expressions". However, expressions are constrained to a subset that semantically makes sense in the context of a generator model. That means that, for example, while you can use the
+ operator to concatenate strings, you cannot use statechart-related constructs like the
after keyword or the
active() built-in function.
In the properties definition section near the top of a generator model, a value assigned to a property must be given as a literal. More complex expressions are not supported there.
There are several built-in read-only variables which can be used in expressions in generator models:
System.getProperty("user.name")).
Please note: Neither USER nor HOSTNAME should be used for any security-related tasks. Especially the username can be spoofed easily, if anyone wanted to.
The values assigned to properties are default values only. That means, you can override them by different values when actually executing a generation model by way of running the headless code generator.
For example, the command to call the headless generator might look like this:
scc -m myGenmodel.sct -v version=2.0;isBeta=false
The name/value pairs specified by the
-v option would override the corresponding default values of the properties in the generator model. In this case, the properties
version and
isBeta would be set to the values “2.0” and “false”, respectively. The variables
projectName and
generateTimerService would maintain their default values as specified in the SGen file.