A state machine is a behavioral model. It consists of a finite number of states and is therefore also called a finite state machine (FSM). Based on the current state and a given input, the machine performs state transitions and generates outputs.
YAKINDU Statechart Tools (itemis) offer an integrated modelling environment for the specification and development of reactive, event-driven systems based on the concept of state machines. You can develop and simulate state machines, generating them as source code for your target software system. YAKINDU Statechart Tools are based on the open-source development platform Eclipse.
The impulse YAKINDU extension integrates the Statechart Tools and enables the signal oriented simulation and visualization of YAKINDU state diagram models. The extension is helpful for the creation (simulation with reference data) as well as for the execution and tracing of the created state machine.
You may want to:
With the State Chart Simulator Production you can feed Yakindu state chart models with online or reference data (e.g. events) and display the result of the simulation (e.g. states and variables) in diagrams and charts.
Instead of a step-by-step procedure, the simulation is carried out in one go. This means if you have simulation data available (not waiting for online data) and modify the state chart, impulse will show you the results immediately, even if the simulation time is minutes or hours.
This tutorial uses a simple statechart example (Elevator) and is intended to provide insights into how the producer can be used in your projects.
To the right , you find the state chart of very simple elevator.
After initialization, the state chart is in idle state, the elevator is on the ground floor and no destination floor is selected. When a new destination floor is requested, the elevator shall start moving. If the elevator is idle above the ground floor and receives no request, it will travel to the ground floor after 5 seconds.
To open and edit the state chart, double-click the elevator.sct from the tutorial wallet (see screen-cast below).
To find the definition of the elevator in impulse, go to “Preferences->impulse->Charts” and double click the Elevator chart.
Below name and description fields you find the mode settings.
To add a new state chart, go back to the chart preferences, click “Add” and select the YAKINDU State chart.
Charts can be used to visualize signals and signal-related information. Charts can be extended and configured in many ways. You can define your own charts or integrate existing chart tools. To add a new chart:
To visualize a signal with impulse, users usually define a plot with a source signal and a diagram type (e.g. a line diagram) and some additional parameters (e.g. the 'annotate' flag). If you select "Chart" as diagram type, you will need to state which chart to use.
The chart preferences page shows all defined charts. Each chart is based on a specific type (chart provider). You can add multiple charts of the same type, but with different presentation options.In the plot, the chart is assigned the signal data. In addition, chart specific plot parameters can be adjusted.
The simulator can be fed with reference/stimulus data or with online data. This allows to test the behaviour of the statemachine with real process data.
With the expandable concept of signal ports any signal source can be connected. This can be simple data connections with a configurable reader, external libraries or complex hardware interfaces.
Multiple signal ports of different types can be combined into one, synchronizing the received signals. By using scripts, connected devices or applications can be stimulated and their events processed. This could be for example a CAN bus message or a byte sequence via TCP.
in this tutorial, however, reference data shall be created with the help of a script. recJs files are text files that contain a signal script to generate the resulting record. Instead of scripts, the Yakindu YET format would be an option (or any other supported format) to stimulate the state machine. This option shall be handled in another tutorial.
To view and modify the script, select 'feed.recJS' and open with the eclipse script editor.
The script generates two signals, A sinusoidal signal which is not used further. And an event signal, that controls our elevator. First, the signals are generated.
The signals are then opened at time 0. First the sine signal is generated and then six events are added for one floor each. Finally, the signals are closed at time 200s.
//-recjs (keep this line) // generator : Record generator (de.toem.impulse.samples.ISingleDomainRecordGenerator) // file : File object of the executed recJs file (java.io.File) // p0..p9 : Parameters from the reader configuration (java.lang.String) // progress : Progress control (de.toem.pattern.threading.IProgress) // console : Console output (de.toem.impulse.scripting.IScriptConsole) // Init the record generator.initRecord("Elevator Feed", TimeBase.ms); // create signals var signals = generator.addScope(null, "Feed Signals"); var floor = generator.addSignal(signals, "floor", "", ProcessType.Discrete, SignalType.Integer, SignalDescriptor.DEFAULT); var sin = generator.addSignal(signals, "sin", "", ProcessType.Discrete, SignalType.Float, SignalDescriptor.DEFAULT); // get writer var floorWriter /*:IIntegerSamplesWriter:*/ = generator.getWriter(floor); var sinWriter /*:IFloatSamplesWriter:*/ = generator.getWriter(sin); // open signals generator.open(0); // create sin var t = 0; for (; t < 200000; t++) { // write time as integer sinWriter.writeDouble(t, false, (100 * Math.sin(t / 1000.0))); } // write signal for (var n = 1; n < 6; n++) { floorWriter.write(n * 30000, 0,n); } // close signals generator.close(200000); // 200 s
If you open the feed.recJS file with the impulse viewer, you find the plot with the production under Elevator Statechart'.
The plot dialog is roughly divided as follows;
On the left side is defined what to display, on the right side how to display it.
Productions are executed on the fly, as soon as the signal data is required for further processing, and re-executed when settings or input signal have been changed.
The result of the state chart production can be used as input for further calculations, e.g. a series of state charts.
The production accepts 0..N input signals.
In case of primitive signals (integer, float, text, enumeration), the signals are bound to (writeable) variables or events of the state chart model with the same name. In case of signals with an event content descriptor, the signals are used as event inputs with multiple events. Struct signals can be used to combine variables and events.
The process type (discrete or continuous) defines how the model is simulated:
The signal type may be:
Select the chart from the combo box. Simulation may be time consuming depending of the model. To avoid system timeout, the timeout parameter may be set.
Styte charts in Impuls can be synchronized with model resources. This enables a short development cycle as you can edit the state chart and simulate it in one go.
Find further script examples below to fire events and set variables.
//-recjs (keep this line) // just run single snippets !!! // EVENTS // Don't be confused with event signal type and event content: // * The Event signal type is just a signal type type with either no value or an enumeration value and can be used for events or other information. // * The content descriptor "ISample.CONTENT_EVENT" of a signal defines what a signal semantically is and is basically just a hint. // In the case of the state chart simulator, signals are used to raise events if: // 1: the signal name equals the event name: The value of the signal is used as the parameter. // 2: the signal has the "CONTENT_EVENT" content descriptor: The signal can contain multiple event typeS ("TIC", "TOC",...). var t=0; generator.initRecord("State Chart Feed", TimeBase.ms); // simple events without a parameter var myEvent = generator.addSignal(null, "myEvent", "", ProcessType.Discrete, SignalType.Event, SignalDescriptor.DEFAULT); var writer /*:IEventSamplesWriter:*/ = generator.getWriter(myEvent); generator.open(t); writer.write(t,false); generator.close(t+100); // simple events with an int parameter (signal name == event name) var myEvent = generator.addSignal(null, "myEvent", "", ProcessType.Discrete, SignalType.Integer, SignalDescriptor.DEFAULT); var writer /*:IIntegerSamplesWriter:*/ = generator.getWriter(myEvent); generator.open(t); writer.write(t,false,5); // parameter 5 generator.close(t+100); // simple events with a string parameter (signal name == event name) var myEvent = generator.addSignal(null, "myEvent", "", ProcessType.Discrete, SignalType.Text, SignalDescriptor.DEFAULT); var writer /*:ITextSamplesWriter:*/ = generator.getWriter(myEvent); generator.open(t); writer.write(t,false,"huhu"); // parameter "huhu" generator.close(t+100); // multiple different events per signal without parameter (signal name must not match any event or variable name) var allEvents = generator.addSignal(null, "allEvents", "", ProcessType.Discrete, SignalType.Event, new SignalDescriptor(ISample.CONTENT_EVENT,ISample.FORMAT_DEFAULT)); var writer /*:IEventSamplesWriter:*/ = generator.getWriter(allEvents); generator.open(t); writer.write(t,false,"tic"); // first event "tic" writer.write(t+1,false,"toc"); // second event "toc" generator.close(t+100); // multiple different events per signal with parameter (signal name must not match any event or variable name) var allEvents = generator.addSignal(null, "allEvents", "", ProcessType.Discrete, SignalType.Struct, new SignalDescriptor(ISample.CONTENT_EVENT,ISample.FORMAT_DEFAULT)); var writer /*:IStructSamplesWriter:*/ = generator.getWriter(allEvents); // create members var event /*:[Lde.toem.impulse.values.StructMember;:*/ = writer.createMembers(2); writer.createMember(event, 0, "Event", ISample.STRUCT_TYPE_LOCAL_ENUM, ISample.CONTENT_EVENTTYPE, -1); writer.createMember(event, 1, "Parameter", ISample.STRUCT_TYPE_INTEGER, ISample.CONTENT_EVENTPARM, -1); generator.open(t); // write events for (var n = 1; n < 6; n++) { event[0].setStringValue("floor"); event[1].setIntValue(n); writer.write(t+n, 0, event); } generator.close(t+100); // VARIABLES // Signals are used to set variables if the signal name equals the variable name. // simple int variable(signal name == variable name) var myVar = generator.addSignal(null, "myVar", "", ProcessType.Discrete, SignalType.Integer, SignalDescriptor.DEFAULT); var writer /*:IIntegerSamplesWriter:*/ = generator.getWriter(myVar); generator.open(t); writer.write(t,false,5); // value 5 generator.close(t+100); // simple string variable(signal name == variable name) var myVar = generator.addSignal(null, "myVar", "", ProcessType.Discrete, SignalType.Text, SignalDescriptor.DEFAULT); var writer /*:ITextSamplesWriter:*/ = generator.getWriter(myVar); generator.open(t); writer.write(t,false,"myText"); // value "myText" generator.close(t+100); // simple float variable(signal name == variable name) var myVar = generator.addSignal(null, "myVar", "", ProcessType.Discrete, SignalType.Float, SignalDescriptor.DEFAULT); var writer /*:IFloatSamplesWriter:*/ = generator.getWriter(myVar); generator.open(t); writer.write(t,false,4.5); // value 4.5 generator.close(t+100);