This example features a simple executable language using GEMOC Java Engine.
It illustrates the GEMOC animation framework on a Finite State Machine language where the concepts of both the syntax and semantic domains are in a single ecore file.
The example focuses on the tooling of a single language (FSM) with the following tools:
- Tree editor,
- Xtext Editor,
- Graphical Sirius Editor and Animator,
- GEMOC Java Engine (Ie. using a Sequential approach)
- Model execution with debugging and animation capabilities
The FSM use as example here is a simple State Machine which is able to read a string and read it as tokens and produce another string.
Note
In this example, the semantic domain is built as an extension of the syntaxic domain and is merged in a single ecore file.
This might not be the case for all languages where the semantic domain may be structurally different from the semantic domain.
You can either install and play with the example or reproduce it yourself from scratch by following the Section 4, “DIY” instructions
Tip
The sources of these projects are also available online on Github.
Install the projects of this language:
- File → new → Examples… → GEMOC K3 FSM Language example (Sequential)
Create and start an eclipse runtime:
- Run → Run Configurations… → Eclipse application > new
In the second Eclipse instance.
Install sample models for the language:
- File → new → Examples… → GEMOC model example for K3 FSM (Sequential)
Run (actually debug ;-) ) one of the provided model using the predefined launch configurations:
- Run → Debug Configurations… → Gemoc Sequential eXecutable Model → one of the K3FSM launch conf such as K3FSM - TwostatesUpcast(abababa).
You can change the string passed to the model in the launch configuration (in the Model initialization argutments field).
The following figure presents :
- the metamodel k3fsm content;
- how the aspects in k3fsm.k3dsa extends the base classes to add operations;
Note
For simplification of the diagrams, the associations that doesn’t represent a containment, are represented as attributes rather than links.
The elements with a green bullet in Figure 39, “K3FSM Metamodel classes and K3 aspects classes.” are par of the semantic domain. This is the part of the model that will change during the execution of a FSM model. Note that some attributes (such as consummedString) may not be mandatory for correct execution but provide nice user feedback in the animation.
By following these instructions, you’ll be able to reproduce this example from scratch.
Note
You may apply some of these steps in a different order. This is only an example of a valid scenario.
- Open the xDSML perspective
- Create a GEMOC Sequential xDSML Project named "org.eclipse.gemoc.example.k3fsm.xdsml"( Menu File → New → GEMOC Sequential xDSML Project) then Next and select Simple Sequential K3 language project → Finish
-
Right click on the project; GEMOC Language → Create Domain Model Project for Language. Name this new project "org.eclipse.gemoc.example.k3fsm". Optionally adapt the nsUri.
- in the .dsl file, fix the ecore entry by removing
platform:/resource/ecoreFilePath,
from the value.
- in the .dsl file, fix the ecore entry by removing
- Edit the ecore file and add the concepts corresponding to the syntaxic domain (ie. reproduce concepts of Figure 40, “FSM Syntaxic domain.”).
-
In the org.eclipse.gemoc.example.k3fsm project, open the genmodel file,
- change the File extensions for the model to
k3fsmxmi
(on the package: K3fsm → K3fsm → section Model) - Right click on the root element then Generate model code; Generate edit code; Generate editor code.
- change the File extensions for the model to
Tip
Variant: You create the ecore project before creating the xDSML project
Menu _File → New → Ecore Modeling Project then in the template wizard of the xdsml project creation, browse and select the ecore file in the project.
- Edit the ecore, to add the concepts for the semantic domain. Ie. the reference currentState, and the attributes unprocessedString, consummedString, and producedString as presented in Figure 39, “K3FSM Metamodel classes and K3 aspects classes.”.
- Add an EAnnotation aspect on the data that will change during the execution. In the FSM, add the EAnnotation on the reference currentState, and the attributes unprocessedString, consummedString and, producedString (aspect is the source of the EAnnotation)
- Reload the genmodel and generate the model, edit and editors.
-
Rigth clik on the xdsml project; GEMOC Language → Create XText Editor Project for Language. Add the genmodel to the list of EPackages; Name this new project "org.eclipse.gemoc.example.k3fsm.xtext"; use "org.eclipse.gemoc.example.k3fsm.K3FSM" as name for the language, and "k3fsm" as extensions.
-
in the xtext editor:
- change the import from "http://www.eclipse.org/gemoc/example/k3fsm" to "platform:/resource/org.eclipse.gemoc.example.k3fsm/model/k3fsm.ecore"
- comment out the line about incomingTransitions (useless as this is a bidirectionnal reference and as we use target to store this information)
- in the GenerateK3FSM.mwe2 file, verify that then genmodel is referenced with a correct uri; someting like this
referencedResource = "platform:/resource/org.eclipse.gemoc.example.k3fsm/model/k3fsm.genmodel"
- right click; Run As → MWE2 Workflow
-
correct the launch configuration generated by xtext:
-
Run → Run Configuration…
- find the configuration named "Launch Runtime Eclipse" and rename it in order to distinguish it from other xtext projects, (for example "Launch Runtime Eclipse for k3fsm")
- change the "Run a product" from
org.eclipse.platform.ide
toorg.eclipse.gemoc.gemoc_studio.branding.gemoc_studio
-
-
-
Rigth clik on the xdsml project; GEMOC Language → Create DSA Project for Language. Next → Select "User Ecore Basic Aspect"; Set "Aspet file name" to "K3FSMAspects".
- in the .dsl file, fix the k3 entry by removing
qualified.class.name,
from the value. - edit the k3fsmAspect.xtend file and add the following methods in the aspects:
- in the .dsl file, fix the k3 entry by removing
package org.eclipse.gemoc.example.k3fsm.k3dsa import fr.inria.diverse.k3.al.annotationprocessor.Aspect import fr.inria.diverse.k3.al.annotationprocessor.InitializeModel import fr.inria.diverse.k3.al.annotationprocessor.Main import fr.inria.diverse.k3.al.annotationprocessor.Step import org.eclipse.gemoc.example.k3fsm.FSM import org.eclipse.gemoc.example.k3fsm.State import org.eclipse.gemoc.example.k3fsm.Transition import org.eclipse.emf.common.util.EList import static extension org.eclipse.gemoc.example.k3fsm.k3dsa.TransitionAspect.* //import static extension org.eclipse.gemoc.example.k3fsm.k3dsa.FSMAspect.* import static extension org.eclipse.gemoc.example.k3fsm.k3dsa.StateAspect.* @Aspect(className=FSM) class FSMAspect { @Step @InitializeModel def void initializeModel(EList<String> args){ _self.currentState = _self.initialState; _self.unprocessedString = args.get(0) _self.consummedString = "" _self.producedString = "" if(_self.unprocessedString.isEmpty) { println("nothing to process, did you forgot to pass parameters to the launch configuration ?") } } @Step @Main def void main() { try { while (!_self.unprocessedString.isEmpty) { _self.currentState.step(_self.unprocessedString) } } catch (FSMRuntimeException nt){ println("Stopped due to "+nt.message) } println("unprocessed string: "+_self.unprocessedString) println("processed string: "+_self.consummedString) println("produced string: "+_self.producedString) } } @Aspect(className=State) class StateAspect { @Step def void step(String inputString) { // Get the valid transitions val validTransitions = _self.outgoingTransitions.filter[t | inputString.startsWith(t.input)] if(validTransitions.empty) { throw new FSMRuntimeException("No Transition") } if(validTransitions.size > 1) { throw new FSMRuntimeException("Non Determinism") } // Fire transition validTransitions.get(0).fire() } } @Aspect(className=Transition) class TransitionAspect { @Step def void fire() { println("Firing " + _self.name + " and entering " + _self.target.name) val fsm = _self.source.owningFSM fsm.currentState = _self.target fsm.producedString = fsm.producedString + _self.output fsm.consummedString = fsm.consummedString + _self.input fsm.unprocessedString = fsm.unprocessedString.substring(_self.input.length) } } class FSMRuntimeException extends Exception { new(String message) { super(message) } }
a method with @InitializeModel annotation in order to use the launch configuration parameters to set up the runtime data when the model execution starts. |
|
a method with @Main annotation that will be used to start the execution. |
|
a method with the @Step annotation that will be observable (ie. the debugger can do a pause when a object instance of this class call this method). |
|
another method with the @Step annotation that will be observable |
-
- Verify that the project org.eclipse.gemoc.example.k3fsm.xdsml has a dependency to the project that contains the K3 aspects ( org.eclipse.gemoc.example.k3fsm.k3dsa)
- Create a project for the graphical editor: File → New → Viewpoint Specification Project
-
Create a representation for edition
- Change viewpoint name to "K3FSMViewPoint", label "K3FSM"
- New Diagram representation; set the ID to "FSM", add the k3fsm.ecore to the list of metamodels; set the domain class to the FSM class. In Advanced, set the title expression to "feature:name"
-
in the Default layer; New Diagram Element → Container; ID = "StateContainer"; Domain class = "State"; Semantic candidate expression =
[self.ownedStates/]
- on the StateContainer; New Style → Gradient
- on the StateContainer; New Contitional style; Predicate expression =
[self.eContainer().oclAsType(FSM).initialState = self/]
; create another gradient in it and set a border size to 4.
-
in the Default layer; New Diagram Element → Element Based Edge; ID = "TranditionEdge"; Domain class = "Transition"; Source mapping = "StateContainer"; source finder expression =
[self.source/]
; target Mapping = "StateContainer"; Target Finder Expression =[self.target/]
- On TranditionEdge/ Edge Style solid, verify the decorators, No decoration for source arrow, and InputArrow for Target Arrow
- On TranditionEdge/ Edge Style solid / Center Label Style 8; Label Expression =
aql:self.getLabel()
- Open the file org.eclipse.gemoc.example.k3fsm.design.Services; add a method: (use Eclipse quick fix to add the import to
import org.eclipse.gemoc.example.k3fsm.Transition;
)
public String getLabel(Transition transition) { final StringBuilder res = new StringBuilder(); res.append(transition.getName()); res.append("\n"); res.append(""); res.append(transition.getInput()); res.append(" / "); res.append(transition.getOutput()); return res.toString(); }
Debug capabilities (breakpoint support, basic highlighting, …)
- Add debug capabilities to the graphical editor: Rigth clik on the xdsml project; GEMOC Language → Create Animator Project for Language. Select Add a debug layer to an existing diagram description → Next → Select your diagram (FSM) → Next → Finish
Domain specific animation
-
in the k3fsm.odesign file; on the diagram FSM → New Diagram element → Additional layer ; ID = "Animation";
- on the animation layer; New Diagram element → Decorations; then New → Mapping based decoration; Name = "Current State"; Mapping =StateContainer; Image Expression = "/org.eclipse.gemoc.example.k3fsm.design/icons/cursor-3-24.png";
Precondition Expression =
service:self.isCurrenState
; -
add an image named cursor-3-24.png in the icons folder of the design project.
- in the plugin.xmlfile editor, add the icons folder to the binary build (ie. in the bin.includes section of build.properties file) .
- on the animation layer; New Diagram element → Decorations; then New → Mapping based decoration; Name = "Current State"; Mapping =StateContainer; Image Expression = "/org.eclipse.gemoc.example.k3fsm.design/icons/cursor-3-24.png";
Precondition Expression =
-
in the k3fsm.odesign file; in FSM Palette; add a User fixed color; name = hasBeenAnimated; select a color (pink for example)
- in the Animation layer; New Customization → Style Customizations; then New Customization → Style Customization; Predicate expression =
service:self.hasBeenActivated
- in this Customization; New Customization → Property customization by selection; applied on = "TransitionEdge > Edge Style solid"; Property Name =
strokeColor
, Value Selection =hasBeenAnimated
- in this Customization; New Customization → Property customization by selection; applied on = (all Gradient stuff related to StateContainer) ; Property Name =
borderColor
, Value Selection =hasBeenAnimated
- in the Animation layer; New Customization → Style Customizations; then New Customization → Style Customization; Predicate expression =
- in K3FSMViewPoint; New Extension → Java Extension; Qualified Class Name =
org.eclipse.gemoc.example.k3fsm.design.services.FsmAnimatorServices
- add a new class org.eclipse.gemoc.example.k3fsm.design.services.FsmAnimatorServices that extends org.eclipse.gemoc.executionframework.extensions.sirius.services.AbstractGemocAnimatorServices with th following content:
public class FsmAnimatorServices extends AbstractGemocAnimatorServices { @Override protected List<StringCouple> getRepresentationRefreshList() { final List<StringCouple> res = new ArrayList<StringCouple>(); res.add(new StringCouple("FSM", "Animation")); return res; } public boolean isCurrentState(EObject o){ if(o instanceof State){ return ((State)o).getOwningFSM().getCurrentState() == o; } else { return false; } } }
declaration of animations layers that must be automatically enabled when launching an execution |
|
addition of a service that is used in the odesign (see the mapping based decoration named "Current State" in the "Animation" layer) |
Also add the line res.add(new StringCouple("FSM", "Animation"));
in the getRepresentationRefreshList() method of FsmDebugServices.java
-
in the Animation layer; New Diagram Element → Container; ID = "FSMRuntimeDataContainer"; Domain Class = FSM; Semantic Candidate Expression =
[self/]
; Children Representation = List;- on FSMRuntimeDataContainer; New Style → Gradient; with label/Label expression =
aql:self.name+ ' processing status'
-
on FSMRuntimeDataContainer; New Diagram Element → Sub Node; with Id = StringToProcess; Domain class = FSM; Semantic Candidate Expression =
[self/]
- on StringToProcess; New Style → Basic shape; with Label/LabelExpression =
aql:' string to process: '+self.unprocessedString
; uncheck "show icon"; Label Alignment = left;
- on StringToProcess; New Style → Basic shape; with Label/LabelExpression =
- on FSMRuntimeDataContainer; New Style → Gradient; with label/Label expression =
Repeat the above steps about "StringtoProcess/unprocessedString" and adapt to "ProducedString/producedString" to create another element in the list for tthis other data
[66] asciidoc source of this page: https://github.com/eclipse/gemoc-studio/tree/master/official_samples/K3FM/docs/K3FSM_example.asciidoc.