Statechart modeling: Simulation and Execution using Yakindu
Original by Abel Armas-Cervantes, Marlon Dumas and Luciano García-Bañuelos (lastyear.pdf)
In this practice session, you will learn how to model, simulate and execute statecharts using Yakindu Statechart Tools (SCT). As an example, we will specify a controller for a light bulb. The practice session is organised in two parts. In the fist part, you will model the basic behaviour, that is, a lamp bulb controlled with a simple switch. In the second part, you will extend the basic statechart to specify a flashing feature.
PART 1: Basic behaviour
1. Download the Yakindu SCT http://statecharts.org/download.html
If you do not receive an e-mail with the download link after filling out the form, you should go back to this page, and then you should be able to download it. You can then configure the License Server to use our floating licenses.
2. Unzip the package and open the application
3. Create a new java project
Create a new Java Project (from File → New → Project) and name it for example "practice1". Once you have created the Java project, we recommend adding a folder ("model") to the project to store your statechart model and the code generation model that we will create later.
4. Create a new Yakindu Statechart Model
Right click on your project and select New → Other → Yakindu SCT → Statechart Model. Choose a name for your model (e.g., lamp.sct) and put it into the folder created in the previous step.
5. Specify the statechart
Let’s now design a statechart for a light bulb that we can turn “on” and “off” by means of a “switch” event. This statechart will serve as a controller of a Java-based User Interface (UI). We will use a single event called “switch” to turn the light off or on. All the declarations of events and operations will be within the scope of an interface, which we will call UI.
- Draw the statechart shown in the figure! Don’t forget to replace the interface declaration (the text on the left-hand side of this picture). Keep in mind that all events and operations are part of that interface, so in the diagram you write
UI.switch
. - You can simulate your state machine by right-clicking on your model and selecting Run as → Statechart Simulation.
- Initially, the active state is off, and you can change to another state by selecting the event “switch” in the simulation view. If you don’t see the simulation view, you can bring it forth via the menu: Window → Show View → Other → Yakindu SCT → Simulation.
6. Defining the operations
Add two operations, called turnOn() and turnoff() to the interface as shown on this picture. Again, you must refer to them in the diagram as UI.turnOn()
and UI.turnOff()
.
The keyword “entry” specifies the actions that are carried out when entering a given state.
Below, we will start creating the UI for our statechart. The interface will consist of a button and a JLabel for displaying an icon. In short, the button will represent the transition switch and it will allow us to traverse over the two existing states. Thus, every time the button is pushed, a different image shall be displayed in the JLabel – this behavior will be specified in the operations turnoff() and turnOn().
7. Create a new Yakindu Statechart Generator Model
Now right click on your model folder and select New → Code generator mode. For uniformity, keep the generator model inside the same folder of your statechart. Pick up a name for your Generator Model and select your statechart when required:
8. Add the timer and runtime service to your code generator model
The feature declaration in the code snippet below allows us to include the timer services, which we will need in the second part of this practice.
GeneratorModel for yakindu::java { statechart lamp { feature Outlet { targetProject = "practice1" // <--- Your Project Name targetFolder = "src-gen" libraryTargetFolder = "src" } feature GeneralFeatures { RuntimeService = true TimerService = true } } }
9. Generate the Java code for integrating your statechart to the GUI swing-based interface we will construct.
By default, Yakindu generates code whenever you save your .sgen file. If not, you can manually generate code by right clicking on your .sgen file and selecting Generate Code Artifacts. A set of Java classes will be generated and put into the folder specified in the .sgen file (parameter targetFolder). This should create a folder called "src-gen" in your project directory. You need to right click on this "src-gen" folder and select Build Path → Use as Source Folder. Alternatively, you can change the “targetfolder” to “src”, which means that the generated code also will be placed in the original “src” directory.
Have a look at this link to read a more detailed explanation about each of the generated Java classes. Make sure you do not modify the generated classes. Any code you add should be in new classes (which we will create below).
10. Create a new Java class for defining the swing-based GUI interface
You can create new classes by right-clicking on a package under the “src” folder. The code below defines a simple interface containing a JPanel that will be the container for an image and a button. Put this new class under package "practice1" (or whatever you named your project). Note the lines that may depend on your chosen names; many such problems can be auto-fixed with Source → Organize Imports.
package practice1; // <-- NB! Your name import java.awt.BorderLayout; import javax.swing.*; import practice1.lamp.ILampStatemachine.SCIUIOperationCallback; // <-- NB! public class InterfaceST implements SCIUIOperationCallback { ImageIcon on = new ImageIcon("bulb-on.png"); ImageIcon off = new ImageIcon("bulb-off.png"); JLabel image = new JLabel(off); JButton switchButton = new JButton("Switch"); public InterfaceST() { JPanel container = new JPanel(new BorderLayout()); container.add(image, BorderLayout.CENTER); container.add(switchButton, BorderLayout.SOUTH); JFrame frame = new JFrame(); frame.add(container); frame.pack(); frame.setVisible(true); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); } }
An error message should appear asking you to implement some missing methods.
Select “Add unimplemented methods” as shown in the picture. The implemented methods correspond to the operations defined in your statechart (i.e., turnOn(), turnoff()). Within those methods you should specify the actions that must be carried out when entering each of these states. The code for the methods “turnoff()” and “turnOn()” is as follows:
@Override public void turnOn() { image.setIcon(on); } @Override public void turnOff() { image.setIcon(off); }
The images to be displayed when turning the lamp on and off are available at: Figures.zip. You have to copy both of these images into the root folder of your project.
Basically, when entering to the state “off” in the statechart, then the image bulb-off.png will be set as the image on the label. The case for state “on” follows analogously.
11. Create a Java class containing a main method:
public static void main(String[] args) { // Create your state machine LampStatemachine sm = new LampStatemachine(); // Create the instance of your interface handler InterfaceST ui = new InterfaceST(); // Add the event listener(s) to the button(s) ui.addEventListener(sm.getSCIUI()); // Link callback methods to statechart sm.getSCIUI().setSCIUIOperationCallback(ui); // Set the timer service (initially commented out) // sm.setTimer(new TimerService()); // Initialize the internal objects of the statechart sm.init(); // Enter the initial state sm.enter(); // Create statemachine with a cycle period of 100 ms. RuntimeService.getInstance().registerStatemachine(sm, 100); }
If you paste this into your file, the command "Organize Imports" is your friend! Finally, the method setEventListener in the class of your interface looks as follows:
public void addEventListener(SCIUI sciui) { switchButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { sciui.raiseSwitch(); } }); }
This method specifies that an event over the button “switchButton” corresponds to the event “switch” in the statechart.
12. Run the Java class containing your main method.
Right click on the class and do Run as → Java application.
Part 2: Flashing lamp
Modify your project to specify a flashing behaviour. The intended behaviour is given by the following statechart:
Note that the transitions flash on and flash off respond to time events (a delay of 1 sec). Draw the statechart shown above and execute the flashing lamp. To get a diagonal arrow, click on the transition and from the menu bar select Diagram → Line Style → Oblique Style Routing.
Now that you have added time events ("after 1s"), go back to your main method and uncomment line 11 that sets the TimerService. (The code for the timer service is only generated if timed events are used in the model.)
Then, modify class InterfaceST to include a second button in the UI. You can call this second button “flash” and put it next to the “switch” button. It's easiest to replace the line adding the single button with a subpanel:
JPanel buttonPanel = new JPanel(); buttonPanel.add(switchButton); buttonPanel.add(flashButton); container.add(buttonPanel, BorderLayout.SOUTH);
You'll need to modify your addEventListener
method to associate this button to the event “flash” (in the same way that we associated the switchButton object to the event “switch”). Then, try the Java application! If the simulation works, but the lamp is not flashing, make sure you did not forget to add the correct entry operations UI.turnOn() and UI.turnOff() in the new states.