K3FSM Example

[66]

1. Presentation

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.

2. Installation

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

3. Language structure overview

The following figure presents :

  • the metamodel k3fsm content;
  • how the aspects in k3fsm.k3dsa extends the base classes to add operations;
K3FSM Metamodel classes and K3 aspects classes.

Figure 40. K3FSM Metamodel classes and K3 aspects classes.


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 40, “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.

4. DIY

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.

4.1. Create the Language project

  • Open the xDSML perspective
  • Create a GEMOC Java xDSML Project named "org.eclipse.gemoc.example.k3fsm.xdsml"( Menu File → New → GEMOC Java xDSML Project) then Next and select Simple Sequential K3 language project → Finish

4.2. Create the Domain concepts

  • Right click on the project; GEMOC LanguageCreate 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.
  • Edit the ecore file and add the concepts corresponding to the syntaxic domain (ie. reproduce concepts of Figure 41, “FSM Syntaxic domain.”).
FSM Syntaxic domain.

Figure 41. 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)
    • change the Base Package for the model to org.eclipse.gemoc.example.k3fsm (on the package: Fsm → Fsm → section All)
    • Right click on the root element then Generate model code; Generate edit code; Generate editor code.

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.

4.3. Add Semantics data and Tree Editor

  • 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 40, “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.

4.4. Add Textual Editor

  • Right click on the xdsml project; GEMOC LanguageCreate 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 AsMWE2 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 to org.eclipse.gemoc.gemoc_studio.branding.gemoc_studio

4.5. Add Semantics operations

  • Right click on the xdsml project; GEMOC LanguageCreate 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:
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									1
	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												2
	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												3
	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												4
	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)
	}
}

1

a method with @InitializeModel annotation in order to use the launch configuration parameters to set up the runtime data when the model execution starts.

2

a method with @Main annotation that will be used to start the execution.

3

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

4

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)

4.6. Add Graphical Editor

  • 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 ElementContainer; ID = "StateContainer"; Domain class = "State"; Semantic candidate expression = [self.ownedStates/]

      • on the StateContainer; New StyleGradient
      • 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 ElementElement 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();
}

4.7. Add Animation and Debug capabilities to the graphical editor

Debug capabilities (breakpoint support, basic highlighting, …​)

  • Add debug capabilities to the graphical editor: Right click on the xdsml project; GEMOC LanguageCreate Animator Project for Language. Select Add a debug layer to an existing diagram description → Next → Select your diagram (FSM) → Next → Finish

This will create a new Sirius project that extends the existing diagram description. The extensions adds to the Sirius editor a Debug layer with:

  • a couple of actions (toggle breakpoint),
  • a java service class allowing to get information about some run state informations
  • color changes related to the service
  • breakpoint decorators

Domain specific animation

  • in the k3fsm.odesign file; on the diagram FSMNew Diagram elementAdditional layer ; ID = "Animation";

    • on the animation layer; New Diagram elementDecorations; then NewMapping 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) .
  • 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 CustomizationStyle Customizations; then New CustomizationStyle Customization; Predicate expression = service:self.hasBeenActivated
    • in this Customization; New CustomizationProperty customization by selection; applied on = "TransitionEdge > Edge Style solid"; Property Name = strokeColor, Value Selection = hasBeenAnimated
    • in this Customization; New CustomizationProperty customization by selection; applied on = (all Gradient stuff related to StateContainer) ; Property Name = borderColor, Value Selection = hasBeenAnimated
  • in K3FSMViewPoint; New ExtensionJava 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() {  1
		final List<StringCouple> res = new ArrayList<StringCouple>();
		res.add(new StringCouple("FSM", "Animation"));
		return res;
	}

	public boolean isCurrentState(EObject o){     2
		if(o instanceof State){
			return ((State)o).getOwningFSM().getCurrentState() == o;
		} else {
			return false;
		}
	}
}

1

declaration of animations layers that must be automatically enabled when launching an execution

2

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 ElementContainer; ID = "FSMRuntimeDataContainer"; Domain Class = FSM; Semantic Candidate Expression = [self/]; Children Representation = List;

    • on FSMRuntimeDataContainer; New StyleGradient; with label/Label expression = aql:self.name+ ' processing status'
    • on FSMRuntimeDataContainer; New Diagram ElementSub Node; with Id = StringToProcess; Domain class = FSM; Semantic Candidate Expression = [self/]

      • on StringToProcess; New StyleBasic shape; with Label/LabelExpression = aql:' string to process: '+self.unprocessedString; uncheck "show icon"; Label Alignment = left;

Repeat the above steps about "StringtoProcess/unprocessedString" and adapt to "ProducedString/producedString" to create another element in the list for tthis other data

Note

This example uses Sirius to define an animation (Cf. Section 3.1, “Define an animation representation for Sirius”) Arbitrarily complex animation can be supported using Engine addons (Cf. Section 3.2, “Define an animation representation using an engine addon” )

5. Additional resources

The following online resources are related to this examples.

Note

as these resources have been written with a previous version of the GEMOC Studio,
their content may need some adaptations to 100% work with the latest version.