Code Generators

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.

General concepts of the state machine code

Execution schemes

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".

Cycle-based execution scheme

In the cycle-based execution scheme, each run cycle consists of two different phases:

  1. In the first phase, incoming events and time events are collected.
  2. In the second phase, runCycle() is executed and processes the events collected in the first phase.

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".

Event-driven execution scheme

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".

Responsibilities of generated code

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.

Thread-safe execution

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.

Configuring a generator

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.

Outlet feature

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

OutEventAPI feature

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


LicenseHeader feature

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

FunctionInlining feature

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


Debug feature

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

Using properties and expressions in generator models

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.

Built-in variables

There are several built-in read-only variables which can be used in expressions in generator models:

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.

Overriding variables in a headless build

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.