itemis CREATE include code generators for Java, C, and C++ out of the box. The code generators are following a “code-only” approach: They are generating all the code that is needed and do not rely on any additional runtime libraries. The generated code provides a well-defined application programming interface and can be integrated easily with any client code. In this tutorial we will generate Java code for a sample statechart modeling the handling of a phone call, the CallHandling example.
The state machine handling a phone call works as follows:
The complete statechart model is shown below:
The CallHandling statechart model
In order to eventually obtain the CallHandling example in the form of executable code, we have to create the model in the statechart editor first, followed by creating a suitable generator model, followed by executing the code generator to create the source code we need.
In the previous section we have seen how to work with the statechart editor. So let’s create a new project now and use the statechart editor to create the CallHandling statechart model as outlined above.
Since we are going to generate Java code, we should use a Java project to host the statechart model. Use File → New → Java Project and follow the New Java Project wizard to create one.
In addition to what we have learned above already, there is one new concept: interfaces.
Statecharts can describe very complex interactions between a multitude of actors and an even bigger multitude of events these actors can receive or trigger. It is therefore good practice to structure such events and associate them with their respective actors. For this purpose itemis CREATE provides the concept of so-called interfaces.
In the CallHandling example, we have two actors: the user and the phone. Let’s model their communication as two interfaces:
We have to enter the respective definitions in textual form into the statechart editor. Here’s how the interface definitions look like:
@EventDriven
@ChildFirstExecution
interface User:
in event accept_call
in event dismiss_call
interface Phone:
var duration : integer
in event incoming_call
As you can see, the Phone interface also has an integer variable duration which will track the duration of the call. The interface definitions above have to go into the statechart editor’s definition block on the left-hand side of the statechart editor.
If everything went well, any error markers in the model are gone. Your model should look like the one in the screenshot below:
The CallHandling statechart modeled in the statechart editor
For code generation, itemis CREATE use a textual generator model called SGen. The generator model holds key parameters for the code generation process and allows for the latter’s customization.
The first step to code generation is to create a new SGen model. Right-click on the model folder in the project explorer and select New → Code generator model from the context menu.
Selecting New → Code generator model in the context menu
The itemis CREATE generator model wizard opens. Change the File name to CallHandling.sgen, then click Next >.
Selecting a filename for the generator model
From the Generator drop-down menu at the top, select itemis CREATE Java Code Generator.
In the statechart tree beneath that menu, check the CallHandling.sct model, then click Finish.
Selecting generator type and statechart model
Now the wizard creates the default SGen model for Java code generation and opens it in an SGen editor. The project explorer on the left-hand side shows the new model file CallHandling.sgen.
The generator model
Here’s the generator model once again as plain text:
GeneratorModel for create::java {
statechart CallHandling {
feature Outlet {
targetProject = "CallHandling"
targetFolder = "src-gen"
libraryTargetFolder = "src"
}
}
}
Let’s have a closer look at the listing above:
create::java
is the unique ID of the Java code generator.
statechart CallHandling { … }
block references the statechart model we want to generate code for.
feature Outlet { … }
block specifies where the generated code artifacts are to be placed:
targetProject
and
targetFolder
define the Eclipse project
CallHandling
as destination for generated code and within that project the
src-gen
folder.
libraryTargetFolder
option which by default is
src
. You can change the folder name or even remove the
libraryTargetFolder
option at all. In the latter case the “library” components are generated into the
targetFolder
.
A statechart reference may contain various configuration features. You will learn more about them later.
The default generator model is insufficient yet. The CallHandling statechart model uses after and every expressions. That is, it is dealing with time events, requiring a timer service to trigger them. We can instruct the code generator to provide us with a default timer service implementation by adding the following feature to the generator model:
feature GeneralFeatures {
TimerService = true
}
What do we have to do to actually start the Java source code generation? Nothing!
The generator model is executed by a so-called Eclipse builder. That is, as long as the Project → Build Automatically menu item is checked (which it is by default), the artifacts are generated automatically with each modification of the statechart model or of the generator model.
As you can see in the project explorer, the folder src-gen has been created and populated with the generated Java source code artifacts.
Adding the timer service feature
Add the generated artifacts to the Java build path by right-clicking on the src-gen folder and selecting Build Path → Use as source folder in the context menu.
Declaring src-gen as a source folder
If you want to execute your generator model manually, select
Generate Code Artifacts from the
.sgen
file’s context menu in the project explorer.
Now that we have a generated Java implementation of the CallHandling state machine available, we want to actually use it from some client code. We’ll create that client code in a second.
Let’s establish a new Java class CallHandlingClient and integrate the state machine with it:
Next, copy the following code into the created class:
package callhandling;
import callhandling.callhandling.CallHandlingStatemachine;
public class CallHandlingClient {
public static void main(String[] args) throws InterruptedException {
// Create the state machine:
CallHandlingStatemachine sm = new CallHandlingStatemachine();
sm.setTimer(new TimerService());
// Enter the state machine and implicitly activate its "Idle" state:
sm.enter();
// Raise an incoming call:
sm.getSCIPhone().raiseIncoming_call();
sm.runCycle();
// Accept the call:
sm.getSCIUser().raiseAccept_call();
sm.runCycle();
// Keep the phone conversation busy for a while:
for (int i = 0; i < 50; i++) {
Thread.sleep(200);
sm.runCycle();
}
// Before hang-up, output the duration of the call:
System.out.println(String.format("The phone call took %d seconds.", sm.getSCIPhone().getDuration()));
// Hang up the phone:
sm.getSCIUser().raiseDismiss_call();
sm.runCycle();
}
}
Let’s have a detailed look at this client code:
CallHandlingStatemachine
constructor:
CallHandlingStatemachine sm = new CallHandlingStatemachine();
ITimer
interface. And since we added the
TimerService
feature to the generator model, the code generator creates a default implementation
com.yakindu.scr.TimerService
that uses the
java.util.Timer
class. A new instance of the default
TimerService
is created and set to the state machine:
sm.setTimer(new TimerService());
sm.enter()
enters the state machine and – via its initial state – activates its
Idle state.
getSCIPhone()
and
getSCIUser()
. You can access all incoming events and all variables via these interfaces.
sm.getSCIPhone().raiseIncoming_call();
raises the
incoming_call event, activating the
Incoming Call state after the next run cycle has been executed. The latter is triggered by
sm.runCycle()
.
sm.getSCIUser().raiseAccept_call()
accepts the call or, to be more precise, raises the
accept_call event via the
User interface. It activates the
Active Call state after the next run cycle has been performed by
sm.runCycle();
.
for
loop, the run cycle is executed periodically every 200 milliseconds. This simulates the duration of the phone call.
sm.getSCIPhone().getDuration()
retrieves the call’s duration, which is then formatted and printed to the console.
sm.getSCIUser().raiseDismiss_call()
raises the
dismiss_call event. It activates the
Dismiss Call state after the next run cycle.
You can execute the client code via Run As → Java Application from the class file’s context menu.