Please look at https://gitlab.inria.fr/glose/flight-controller-language-demonstrator for all the sources (including both the FCL language and this document).

The latest online version of this document: as html, as pdf

The goals of this demonstrator is to provide general guidelines about how to use GEMOC technologies in order to build the tooling for a language.

The provided tooling covers several concerns such as:

  • Model edition (including textual and graphical edition)

  • Model execution (including model debugging).

It is illustrated on a realistic language: a language for designing drone flight controllers.

As explained in https://download.eclipse.org/gemoc/docs/nightly/index.html , building the tooling for a language highlights 2 mains roles: language engineers and modellers.

  • Language engineers are the users that design executable DSLs using a metalanguage to define their execution semantics. These users also develop domain- specific tools for their languages, such as editors, validators, compilers…​ When possible they reuse and/or extend generic tools provided by metalanguage engineers.

  • Modelers are the users that design models conforming to executable DSLs. Depending on how supported a given executable DSL and the metalanguage used to define its execution semantics are, modelers will have access to a number of tools to aid them in their endeavor.

The GEMOC Studio used in this demonstrator offers dedicated workspaces and tooling for each of these roles that are referenced as GEMOC Language workbench for the tools for language engineers and GEMOC Modeling Workbench for the tools for _Modelers.

In this document, we may have to switch between these workbench in order to show the result of the language workbench in the final modeling workbench:

  • items or roles relative to the Language Workbench or involving the Language Engineer are identified with the following icon : IconeGemocLanguage 32

  • items or roles relative to the Modeling Workbench or involving the Modeler are identified with the following icon : IconeGemocModel 32

This switch is required only during the development, once finished, one can build a standalone eclipse product dedicated to the Modelers that contains only the final dsl tooling.

For FCL, one can use the following update site to add FCL tooling to an existing Eclipse and get a Modeling workbench.

This document presents the actions in a sequential order corresponding roughly to the first time each of these actions was started. However, the development is actually iterative. Actions of the last steps may require changing or improving element done in previous steps.

1. Flight Controller Language - FCL

FCL Icon 96

1.1. Language Overview

When building a flight controller, most people directly write the controller logic in the target platform language. For example, they write in C when targeting ardiuno hardware.

Thanks to GEMOC, we build a language dedicated to the specification of the logic for such code.

An end user of such language (i.e. modeler) would then have a higher level of abstraction and better editing support allowing her to be more efficient when writing complex logic.

The Flight Controller Language (FCL) developed for this demonstrator is intended to specify relatively small functions (which might be complex in terms of control command laws on the rotors and sensors) and basic plans. These functions communicate data between them in order to create a data flow.

In order to adapt the behavior of the system to different situations, it uses a mechanism of modes. The modes filter the data flow functions that are enabled or not. Switching from a mode to another is driven by an automata.

For instance, typical basic modes that would be defined for a quadcopter would be inspired by the ones defined in ardupilot flight controller http://ardupilot.org/copter/docs/flight-modes.html i.e. a Stabilized mode (Self-levels the roll and pitch axis), Acrobatic mode (Holds attitude, no self-level), Land (Reduces altitude to ground level, attempts to go straight down), etc.

Figure 1 shows an example of mode automata with ManualStabilizeFlight mode as initial mode, ManualAcrobaticFlight as a second mode and a final Stopped mode.

Figure 2 shows an example data flow with various functions. Some of these functions are enabled only in some modes as shown in Listing Mini quadcopter controller enabled functions (excerpt of MiniQuadCopter_demo.fcl).

MiniQuadCopter01 FlightController Mode Diagram
Figure 1. Mini quadcopter controller mode diagram
MiniQuadCopter01 FlightController Function Diagram
Figure 2. Mini quadcopter controller function diagram
Listing Mini quadcopter controller enabled functions (excerpt of MiniQuadCopter_demo.fcl)
FlightControllerModel QuadCopterCtrlr {
    mainModeStateMachine {
        mode ManualAcrobaticFlight {
            enabledFunctions (
                FlightController,
                FlightController.getPilotDesiredThrottle,
                FlightController.getPilotDesiredYawAngularVelocity,
                FlightController.getPilotDesiredRollAndPitchLeanAngle,
                FlightController.getAcrobaticDirectiveToEnginePower,
                Environment,
                VirtualSensors
            )
        }
        mode ManualStabilizedFligth {
            enabledFunctions (
                FlightController,
                FlightController.getPilotDesiredThrottle,
                FlightController.getPilotDesiredYawAngularVelocity,
                FlightController.getPilotDesiredPitchAngularVelocity,
                FlightController.getPilotDesiredRollAngularVelocity,
                FlightController.getStabilizeDirectiveToEnginePower,
                Environment,
                VirtualSensors
            )
        }
        final mode Stopped{}
        initialMode ManualStabilizedFligth
[...]

1.2. Repository Content Summary

The sources in git contains the elements decrisbed in the following subsections:

1.2.1. FCL Specifications projects

The specifications of FCL are in fcl-implementations.

The folder fcl contains the projects building a language that is not executable yet. This is a base for the behavior implementations (both for the ALEFCL implementation that is sequential and for the ConcurrentFCL that will be developed for D2.1)

The folder commons contains the projects that are not specific to FCL

The folder alefcl contains the projects defining an executable language for FCL writing using ALE.

The folder releng contains projects used for release engineering. i.e. specification about how to package and deploy the FCL language in an Eclipse installation. (cf. Section 2.6)

1.2.2. Demonstration Controllers

Some FCL model examples are located in the model_examples folder.

The demonstrator’s main example is located in fr.inria.glose.miniquadcoptercontroller.demo_v0. It models a Quadcopter flight controller with two modes: a manual acrobatic mode and a manual stabilized mode as shown in Figure 1 and Figure 2.

2. Scenario #1: Building an Executable Modeling Language

This scenario demonstrates the ability of the GEMOC Studio to help a language engineer to build her own modeling language, including the concepts (aka. abstract syntax), the textual and/or graphical representation (aka. concrete syntax), and the meaning of those concepts over the time (aka. behavioral semantics). The behavioral semantics is specified in the form of a virtual machine allowing the execution of a conforming model.

As an outcome of such a scenario, the demonstrator shows how to automatically get a domain-specific modeling and execution environment from the specification of the modeling language. The use of resulting environment is demonstrated in Chapter 3

2.1. FCL Metamodel Design

During this phase, the IconeGemocLanguage 16Language engineer use Ecore Tools to capture the concepts the IconeGemocModel 16 Modeler will be able use.

The main concerns of the IconeGemocLanguage 16Language engineer during this step will be to specify a class diagram with a special focus on:

  • the list of concepts

  • their relationships.

This includes the containment relationship which is used by many of the generic tools in order to provide efficient default implementations.

2.1.1. FCL Ecore

Basically, FCL allows to model a DataFlow where the enabled Functions are filtered by a StateMachine defining "Modes".

Figure 3 shows the relations between the major concepts:

  • FCMModel, the root of the model;

  • ModeStateMachine, the automata controlling modes;

  • Function, the functions of the dataflow; The functions can contains sub-functions. The topmost function (contained by the FCMModel) is considered as the main function.

  • FunctionPort, the input and output port of functions;

  • FunctionConnector, the connections between ports;

  • Event, the event that can be used to trigger mode change;

  • DataType, the primitive type of the data used in the model;

  • Action, the action done when processing functions or when triggering mode changes;

  • Expression, the expression used in actions and in the state machine guards.

FCL main class diagram
Figure 3. FCL main class diagram

Figure 4 shows the ModeStateMachine concepts. Basically, it models an automata controlling which Functions are enabled. The ModeStateMachine is composed of Modes that enable a set of Function. Changing mode is done by going through Transition where these Transition are guarded by an Event and/or an Expression. Some Action can be defined when a Transition is fired or when entering or leaving a Mode.

StateMachine class diagram
Figure 4. Mode automata class diagram

Figure 5 shows the Function concepts. Basically, it models a data flow between Functions. These Functions declare DataPorts that can be connected through FunctionConnectors. Each Port may be In, Out or InOut. A Function can declare sub functions.

FMUFunction and FMUFunctionBlockDataPort are specializations dedicated to handle the connect a blackbox Function that can communicate using FMI/FMU protocol.

A Function declares an Action whose main role is to compute the value to set on output ports based on the values of input ports.

Functions can declare a Variable that is local to the function. It allows to use it (read/write) from Expressions and Action (i.e. Event, procedure body, …​). Modification of a variable is allowed to the current function or its children. A variable can also be modified by the dataflow if the variable is associated to a port. It can also be part of a FunctionTimeReference. In that case, the variable is automatically incremented when the function has been executed.

BlockFlow class diagram
Figure 5. BlockFlow class diagram

Figure 6 shows the concepts supported to define the Expression in various places in the DSL. Expression are typed, this allows several type verification either statically and at runtime (see also Figure 8).

BinaryExpression defines a set of operators allowing to combine 2 expressions.

UnaryExpression defines some simple operation on a given expression.

CallConstant allows to define constant in the model (for each of the supported data type).

CallDeclarationReference allows to get the current value associated to one of the CallableDeclaration elements of the language: a port of a Function (FunctionPort), a local variable declaration (VarDecl), a variable in a function (FunctionVarDecl) or an enumration literal.

Similarly to CallDeclarationReference, CallPrevious allows to get the previous value associated to one of the CallableDeclaration elements of the language.

Expression class diagram
Figure 6. Expression class diagram

Figure 7 shows the concepts supported to define the Action in various places in the DSL. It basically uses Expression in order to provide the value to set in function ports and function variables.

Action class diagram
Figure 7. Action class diagram

Figure 8 shows the primitive types that the IconeGemocModel 16 Modeler can use when she defines types in the model. These types allow validation and early error reporting when associated with validation rules (see Section 2.2.4).

Types class diagram
Figure 8. Types class diagram

2.1.2. Basic Tree Editor

Up to this point, the Ecore metamodel provides a minimalistic model edition support using a tree representation. Figure 9 shows this tree editor opened on the basic quadcopter controller (same model as in Figure 1).

DemoController in TreeEditor Screenshot
Figure 9. DemoController model in tree editor screenshot

This tree editor is convenient during the early steps of the ecore definition as is allows to create sample models and manually check the expressivity of the language.

Even if this demonstrator focuses on more sophisticated editors (Textual and graphical) (see Section 2.2), it is easy to customize the basic tree editor to provide nicer labels and icons.

Simply do a generate Model edit and generate Model editor on the genmodel file. Then change the gif images and edit the getImage and getLabel methods in the xxxItemProvider java classes.

Do not forget to add a Generated NOT in the comment of the modified methods to prevent overriding of your code if you regenerate the code due to an update in the metamodel.

2.2. Editors Design

The demonstrator illustrates the development of two related and complementary editors:

  • a textual editor allowing to fully write the model in a modern text editor with completion, syntax highlighting, error/warning reporting.

  • a graphical editor allowing to display and edit some part of the model. This editor doesn’t intend to provide a graphical representation for all concepts but will open the textual editor when required. (for example, the Expression or Action part of the model are displayed/edited using a textual syntax only)

2.2.1. Xtext Editor

The demonstrator implements a textual editor for FCL using Xtext. Xtext is a framework based on a grammar language that produces an infrastructure with editing support for Eclipse. This infrastructure also offers interesting additional supports such as parser, linker, typechecker, or compiler support.

Starting an Xtext editor for FCL is done by doing:

  • Right Click on the dsl project

  • GEMOC LanguageCreate Xtext editor project for Language

    • project name: fr.inria.glose.fcl.xtext

    • Name: fr.inria.glose.fcl.xtext.FCL

You can also do a Filenewprojectxtext project from existing ecore models

This creates the project: fr.inria.glose.fcl.xtext

The file fr.inria.glose.fcl.xtext/src/fr/inria/glose/fcl/FCL.xtext contains the grammar allowing to parse *.fcl files.

The default grammar rules generated by the project creation wizard are functional but doesn’t suit our needs as they provide a syntax that is too verbose. They are customized to provide a friendlier textual syntax.

For example Listing XTtext rules for Action show some of the rules related to the Action part of the metamodel.

Listing XTtext rules for Action
Action returns Action:
    ActionBlock | Assignment | DataStructPropertyAssignment | IfAction | WhileAction | ProcedureCall | VarDecl_Impl | FCLProcedureReturn | Log;

ActionBlock returns ActionBlock:
    {ActionBlock}
    '{'
        ( actions+=Action (  actions+=Action)*  )?
    '}';

Assignment returns Assignment:
    assignable=[Assignable|QualifiedName] ':=' expression=Expression
    ;
IfAction returns IfAction:
    'if' condition=Expression
    'then' thenAction=Action
    (=>'else' elseAction=Action )?
    ;

After having changed the Xtext rules, a Right click on the xtext file → Run AsGenerate Xtext artifacts will update the java code for the parser.

Reference documentation for writing Xtext grammar rules: https://www.eclipse.org/Xtext/documentation/301_grammarlanguage.html

After modification (see fr.inria.glose.fcl.xtext/src/fr/inria/glose/fcl/FCL.xtext), it now supports operator precedence, and a syntax less verbose than the default one.

Xtext allows to define formatting rules. These rules format the spaces, indentation and line breaks when using Ctrl+Shift+F in the editor. The formatter code in conveniently written in the form of rules in FCLFormatter.xtend

While providing formatting rules is only a minor addition when using only Xtext editor, this becomes important to have it if the graphical views also allow to edit the model. This ensures that the newly created elements will be properly displayed in the textual editor.

IconeGemocModel 16 Section 3.1.1 shows the resulting Xtext editor in action.

2.2.2. Sirius Editor

The demonstrator implements a graphical editor for FCL using Sirius. Sirius is a framework based on a diagram specification language that provides graphical model edition support for Eclipse.

To initiate a first diagram for FCL:

  • Right Click on the dsl project

  • GEMOC LanguageCreate Sirius editor project for Language

    • project name: fr.inria.glose.fcl.design

  • NextNext → check the box create a plugin using one of the following templates → Select FCLModel as root container → Finish

The Sirius diagram specification is in fr.inria.glose.fcl.design project.

Similarly to the default grammar produced by Xtext, this default diagram needs some improvements. The specifications for diagrams is located in the fcl.odesign file.

The improved odesign file of the demonstrator defines two diagrams:

  • one diagram dedicated to show the Mode automata

  • one diagram dedicated to show the DataFlow between the Function

Figure 10 shows the part of the odesign file specifying the automata. The property view shows how is configured the selected element. In the figure the TransitionEdge create an Edge linking the source attribute to the target attribute of Transition. This edge is represented using a Solid line. The ModeContainer uses several conditional style definition in order to customize the presentation according to various criterion (initial state, final state, …​)

Sirius Mode design Screenshot
Figure 10. FCL Mode diagram specification screenshot

Figure 11 shows how the dataflow diagram is specified. It uses containers for the Functions as they can be nested. The ports are placed on the border of the container thanks to BorderedNode. Again, several conditional styles are used to differentiate the Function from FMUFunction. The dataflow diagram also defines several Sections that contains tools. A tool is actually an action that is added to the editor. For this diagram, the sections define creation actions allowing to create new element or connect them with the mouse.

Sirius DataFlow design Screenshot
Figure 11. FCL DataFlow diagram specification screenshot

Sirius uses queries in several places of the odesign file in order to compute various elements. For example to compute label strings or to navigate to a set of model elements. (for example in Figure 11, self.eContents() returns the list of all contained model elements of self) Writing queries directly in the fields of Sirius editor is sometime difficult to debug in case of complex query. For this reason, several of the expressions in the odesign are simplified by using a java service. The services are defined in fr.inria.glose.fcl.design/src/fr/inria/glose/fcl/design/Services.java. They’ll be easier to debug than internal queries as the classic java debugger will allow to inspect them in case of trouble.

IconeGemocModel 16 Section 3.1.2 shows the resulting Sirius editor in action.

Similarly to programming where several programs may produce the same behavior, it exist several ways to write Sirius diagram specification that draw the same diagram.

2.2.3. Sirius/Xtext Integration

The demonstrator implements several useful features in order to improve the collaboration of Xtext and Sirius editors: - use of Xtext serialization and formating in order to get the text to be used in Sirius (in labels or in popups). - navigation from the graphical editor to the textual editor. (ie. jump to the line in the textual editor corresponding to the selected graphical element)

The java service fr.inria.glose.fcl.design/src/fr/inria/glose/fcl/design/Services.java defines several methods that helps Sirius/Xtext collaboration. For example, the xtextPrettyPrint() (see Listing xtextPrettyPrint java service) that allows to get the Xtext representation of an EObject (including its contained element) is very handy to represent Expression in labels.

Listing xtextPrettyPrint java service
public String xtextPrettyPrint(EObject any) {
    if (any != null && any.eResource() instanceof XtextResource && any.eResource().getURI() != null) {
        String fileURI = any.eResource().getURI().toPlatformString(true);
        IFile workspaceFile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileURI));
        if (workspaceFile != null) {
            ICompositeNode node = NodeModelUtils.findActualNodeFor(any);
            if (node != null) {
                return node.getText().trim();
            }
        }
    }
    return "";
}

The method openBasicHoveringDialog(EObject any) is convenient to open a popup window for long text that doesn’t fit in a label. This is done via a the definition of a tool in the diagram section specification (see Section 2.2.2).

The method openTextEditor(EObject any) that allows to open the Xtext editor is useful to open the selected element in the text editor. As the internal EMF resources are connected, saving changes done in the text editor are automatically taken into account in the graphical editor.

Xtext/Sirius collaboration work correctly only if the user doesn’t try to modify the model both in the textual editor and the graphical editor at the same time. She must save the model before trying to edit using another editor.

2.2.4. Static Validation

The structure of the metamodel defined in Section 2.1 already provides some guidance to the IconeGemocModel 16 Modeler in order to build correct models. However, this structure cannot capture all non-valid situations. Many of them can be checked early by writing validators. (see comments in the various diagrams of the metamodel)

Several techniques are possible (OCL, Sirius validation rule, Xtext validation framework, EMF validation framework).

In this demonstrator, as it relies on Xtext for the serialization, we’ve implemented a set of validation rules using the Xtext framework. They are in the file fr.inria.glose.fcl.xtext/src/fr/inria/glose/fcl/validation/FCLValidator.xtend. It contains validation rules that cover:

  • type checking of Expression and Action

  • checking of call/assignment of properties in complex data types

  • checking of number of parameter when calling a Procedure

  • checking of initial value on variables

  • checking of loop connection (i.e. InOut ports that have a connection to themselve which are valid only if the connector is delayed, that it, its value is transmitted only when finishing the parent function)

Many other useful validation rules could be written, the IconeGemocLanguage 16Language engineer can write rule detecting recurrent errors that many IconeGemocModel 16 Modelers do.

In some case, the check might be only some advice reported as warning.

2.3. Behavioral Semantic

The FCL language intends to be executable, i.e. supporting the execution of the conforming models. In the demonstrator, the semantics of the behavior is developed in ALE.

ALE is a language to complement Ecore metamodels with a behavioral semantics. Concretely, ALE allows to “re-open” the EClasses of a given Ecore metamodel to implement existing EOperations and weave new features or new operations.

The behavioral semantics is built as an extension of the Ecore metamodel defined in Section 2.1 thanks to the aspect mechanism of ALE.

Thanks to the behavioral semantics defined in ALE, it is straightforward to write an interpreter that implement a simulator for FCL models. However, the description of the complete development of a simulator based on the ALE behavioral semantics will be done in deliverable DP1.2: Simulation and Compilation Environment for Flight Control Management at T0+12.

The behavioral semantics is defined in the projects in fcl-implementations/alefcl

The main entry point is the fr.inria.glose.fcl.alefcl.xdsml/model/Alefcl.dsl file that defines the concerns of the language as shown in Listing Alefcl.dsl.

This file defines three kind of elements:

  • a structural static part

  • runtime data (i.e. dynamic information that can change during the model execution)

  • operations (or rules) that manipulate the runtime data according to the static and dynamic parts.

Listing Alefcl.dsl
name = fr.inria.glose.fcl.alefcl.Alefcl                                         (1)
ecore = platform:/resource/fr.inria.glose.fcl.model/model/fcl.ecore, \          (2)
    platform:/resource/fr.inria.glose.fcl.alefcl.vm.model/model/alefcl_vm.ecore (3)
ale=platform:/resource/fr.inria.glose.fcl.alefcl.xdsml/model/Alefcl.ale         (4)
1 Name of the language,
2 Ecore metamodel of the static structure of the model,
3 Ecore metamodel that defines some of the runtime data using Ecore
4 a ALE file that defines:
  • runtime data,

  • operations that implement the expected behavior by manipulating the runtime data.

ALE is able to define all runtime data. However, in some situations, the runtime data can benefit from being placed in a separate Ecore. (For example, to present a complex runtime data structure as a diagram) The file alefcl_vm.ecore illustrates this.

Listing Alefcl.ale entrypoint shows the starting point of the behavior defined in fr.inria.glose.fcl.alefcl.xdsml/model/Alefcl.ale.

Listing Alefcl.ale entrypoint
behavior alefcl;

//Import external Java services
use fr.inria.glose.fcl.alefcl.xdsml.services.FCLService;
use fr.inria.glose.fcl.alefcl.xdsml.fmu.services.FMUService;

open class fcl.FCLModel {                                   (1)

    1 ..* alefcl::EventOccurrence receivedEvents;           (2)
    0 ..* fcl::ProcedureContext procedureStack;

    Integer elapsedTime;
    Boolean sigTermReceived;

    String indentation; // used to improve console display

    @main
    def void main() {                                       (3)
        self.devInfo('-> main() '+self.name);
        // while not on a final mode
        while(not self.modeStateMachine.currentMode.final){
            self.doMainLoop();
            // no one will update currentValues anymore,
            self.dataflow.updatePrevValues();
        }
    }

    @step
    def void doMainLoop(){                                  (4)
        self.info('##################################################');

        Sequence(fcl::Transition) fireableModeTransitions :=
            self.modeStateMachine.fireableModeTransitions().transitions;

        while(fireableModeTransitions->size() >0 or self.receivedEvents->size() > 0){
            self.devInfo(self.indentation+
            '\t fireableTransitions='+fireableModeTransitions+ ' (out of '+self.modeStateMachine.currentMode.outgoingTransition+')');

            if(fireableModeTransitions->size() >0){
                self.modeStateMachine.switchMode(fireableModeTransitions->first());
                // re-evaluate possible mode switch
                fireableModeTransitions := self.modeStateMachine.fireableModeTransitions().transitions;
            }
            while(fireableModeTransitions->size() = 0 and self.receivedEvents->size() > 0){
                // flush events that cannot be directly used to switch mode
                self.devInfo('no more transition can be fired, but some event remains: consume one');
                self.consumeEventOccurence();
                fireableModeTransitions := self.modeStateMachine.fireableModeTransitions().transitions;
            }
        }

        self.devInfo('no more mode switch are possible and all events have been consumed');
        self.devInfo('current mode is '+self.modeStateMachine.currentMode.name);
        self.devInfo('with the following enabled functions:\n\t'+self.modeStateMachine.currentMode.enabledFunctions->collect(f|f.getQualifiedName())->sep('\n\t'));
        self.info('### Evaluate Main dataflow '+self.dataflow);

        self.dataflow.evalFunction();                                   (5)

        self.dataflow.sendDataThroughAllDelayedConnectors();            (6)


    }
    @init
    def void initializeModel(String input) {                            (7)
        self.modeStateMachine.currentMode := self.modeStateMachine.initialMode;

        // init default values of functionVars and connectors
        self.dataflow.initFunctionVars();
        self.dataflow.initPortsDefaultValues();

        self.dataflow.updatePrevValues();

        // init FMUs
        for(fmuFunction in self.dataflow.eAllContents(fcl::FMUFunction)){
            fmuFunction.initializeFMURuntime();
        }

        // TODO optional load of a kind of scenario of input events...
    }
1 reopen class FCLModel defined in fcl.ecore in order to add new attributes and operations
2 new attributes that will be part of the runtime data: a list of EventOccurence received by the system and a contextual stack used by the behavior of fcl.Procedure.
3 the main rule that is launched when executing a model. It basically does a loop of the until the state machine reaches a final state. On each iteration, it calls the doMainLoop() operation and manages the storage of previous values (required to implement the prev function supported by FCL)
4 an operation gathering the main loop behavior. This operation is annotated with @step, which means that if the execution is run in debug mode, this operation is observable. It can pause or be used as part of a breakpoint.
5 call to the evaluation of the main function.
6 all delayed connections are processed once all the functions have been evaluated
7 this method is launched before starting the execution. Its role is to initialize the runtime data according to the model default values. Optionally, launch arguments can be used to parameterize this model initialization.

The operation doMainLoop() firstly evaluates all the possible mode switches. A mode switch may consume event, if after having processed all the possible mode switches some EventOccurence remains, they are flushed. The operation then evaluates the main Function, i.e. it computes the data flow. As shown in Listing Alefcl.ale fcl.Function.evalFunction() this main Function processes its internal functions recursively after having sorted them using a topological sort.

Listing Alefcl.ale fcl.Function.evalFunction()
open class fcl.Function {

    @step
    def void evalFunction(){                                            (1)
        self.info('evaluating Function '+self.functionName()+'...');

        if(self.timeReference <> null){                                 (2)
            self.devInfo('Increment time reference for function '+self.functionName());
            fcl::FunctionVarDecl timeVariable := self.timeReference.observableVar;
            timeVariable.assign(
                    timeVariable.currentValue.plus(
                        self.timeReference.increment.evaluate()
                    )
            );
        }

        if(self.subFunctions->size() > 0){
            self.sendInputDataToActiveInternalFunctions();              (3)

            // runtocompletion of nested functions
            // topological sort of functions
            alefcl_vm::FunctionSequence sortedF := self.sortedSubfunctions();   (4)
            self.info('   will evaluate functions in the following order: '+sortedF.functions->collect(f|'"'+f.name+'"')->sep(', '));


            for(f in sortedF.functions){
                f.evalFunction();                                       (5)
            }
        }

        if(self.action != null) {
            fcl::ProcedureContext procedureContext := fcl::ProcedureContext.create();
            // push context
            self.eContainer(fcl::FCLModel).procedureStack +=  procedureContext;
            // run the action in this context
            self.action.doAction();                                     (6)
            // pop context
            self.eContainer(fcl::FCLModel).procedureStack -=  procedureContext;
        }

        // for all non delayed outgoing connections, send data
        self.sendDataToActiveNonDelayedPorts();                         (7)

    }
1 evalFunction() operation is added to fcl.Function defined in fcl.ecore
2 a function may declare a clock, it is incremented here
3 send input data to the ports of active internal sub functions.
4 make sure to run internal sub function in the correct order using a topological sort. this takes care to ignore functions that are not enabled for the current mode.
5 concrete evaluation of sub functions
6 evaluate the action associated to the current function. This is done in context stack.
7 send the data to the active ports of function connected to this one. Ignore delayed connections that are processed in fcl.FCLModel.doMainLoop() see Listing Alefcl.ale entrypoint)

Listing Alefcl.ale current and previous values shows that current values and previous value are added as runtime data directly on the relevant concepts of fcl.ecore: FunctionBlockDataPort and FunctionVarDecl.

Listing Alefcl.ale current and previous values
open class fcl.FunctionBlockDataPort extends fcl.FunctionPort{
    fcl::Value previousValue;
    fcl::Value currentValue;
[...]

open class fcl.FunctionVarDecl {
    fcl::Value previousValue;
    fcl::Value currentValue;
[...]

Listing Alefcl.ale binary expression evaluation shows the principle of Expression evaluation. BasicOperatorBinaryExpression.evaluate() implements the operators by delegating to the corresponding operation in Expression. Most of the operations in Expression are logically abstract (i.e. the body actually raises an exception inviting the IconeGemocLanguage 16Language engineer to implement the possibly missing operation) as they are actually implemented on each sub class (IntegerValue, BooleanValue, etc)

Listing Alefcl.ale binary expression evaluation
open class fcl.BasicOperatorBinaryExpression extends fcl.BinaryExpression {
    //override DataValue evaluate() {
    def fcl::Value evaluate() {
        self.devInfo(self.eContainer(fcl::FCLModel).pushIndent()+
            '-> BasicOperatorBinaryExpression.evaluate '+self);
        if(self.operator = fcl::BinaryOperator::OR) {
            fcl::BooleanValue bValue := fcl::BooleanValue.create();
            bValue.booleanValue := self.lhsOperand.evaluateAsBoolean() or self.rhsOperand.evaluateAsBoolean();
            result := bValue;
        } else if(self.operator = fcl::BinaryOperator::AND){

            fcl::BooleanValue bValue := fcl::BooleanValue.create();
            bValue.booleanValue := self.lhsOperand.evaluateAsBoolean() and self.rhsOperand.evaluateAsBoolean();
            result := bValue;
        } else {
            // all other operators need both lhs and rhs evaluation
            fcl::Value lhs := self.lhsOperand.evaluate();
            fcl::Value rhs := self.rhsOperand.evaluate();
            if(self.operator = fcl::BinaryOperator::EQUAL){
                result := lhs.equals(rhs);
            } else if(self.operator = fcl::BinaryOperator::PLUS){
                result := lhs.plus(rhs);
            } else if(self.operator = fcl::BinaryOperator::MINUS){
                result := lhs.minus(rhs);
            } else if(self.operator = fcl::BinaryOperator::MULT){
                result := lhs.mult(rhs);
            } else if(self.operator = fcl::BinaryOperator::DIV){
                result := lhs.div(rhs);
            } else if(self.operator = fcl::BinaryOperator::GREATER){
                result := lhs.greater(rhs);
            } else if(self.operator = fcl::BinaryOperator::LOWER){
                result := lhs.lower(rhs);
            } else if(self.operator = fcl::BinaryOperator::GREATEROREQUAL){
                result := lhs.greaterOrEquals(rhs);
            } else if(self.operator = fcl::BinaryOperator::LOWEROREQUAL){
                result := lhs.lowerOrEquals(rhs);
            } else {
                self.raiseException('not implemented, please implement evaluate() for '+self);
            }
        }
        self.eContainer(fcl::FCLModel).popIndent();

    }
}
open class fcl.Value {
    //override DataValue evaluate() {
    def fcl::Value evaluate() {
        self.devError('not implemented, please ask language designer to implement evaluate() for '+self);
        self.raiseException('not implemented, please implement evaluate() for '+self);
    }
[...]

    def fcl::Value plus(fcl::Value rhs) {
        self.devError('not implemented, please ask language designer to implement plus() for '+self);
        self.raiseException('not implemented, please implement plus() for '+self);
    }
    def fcl::Value minus(fcl::Value rhs) {
        self.devError('not implemented, please ask language designer to implement minus() for '+self);
        self.raiseException('not implemented, please implement minus() for '+self);
    }
    def fcl::Value mult(fcl::Value rhs) {
        self.devError('not implemented, please ask language designer to implement mult() for '+self);
        self.raiseException('not implemented, please implement mult() for '+self);
    }
[...]
}
open class fcl.IntegerValue {
    def fcl::Value evaluate() {
        self.devInfo(self.eContainer(fcl::FCLModel).indentation+
            ' -> IntegerValue.evaluate '+self);
        result := self;
    }
    def fcl::Value copy() {
        fcl::IntegerValue aValue := fcl::IntegerValue.create();
        aValue.intValue := self.intValue;
        result := aValue;
    }
    def fcl::BooleanValue equals(fcl::Value rhs) {
        fcl::BooleanValue bValue := fcl::BooleanValue.create();
        bValue.booleanValue := self.intValue = rhs.toIntegerValue().intValue;
        result := bValue;
    }
    def fcl::BooleanValue lower(fcl::Value rhs) {
        fcl::BooleanValue bValue := fcl::BooleanValue.create();
        bValue.booleanValue := self.intValue < rhs.toIntegerValue().intValue;
        result := bValue;
    }
[...]

As an overview of DP1.2, running this operational semantics using a GEMOC based engine will produce executions similar to Figure 12.

Where self.devInfo(), self.info() and self.important() messages are logged in the Console View in green, black and blue respectively. Other views are used to interact with the execution or display values.

DP12 run overview Screenshot
Figure 12. Demo controller model running and paused after having done one doMainLoop().

2.4. FCL Animation

Preliminary version of the section, part of DP1.2

The Sirius diagram animation specification is in fr.inria.glose.fcl.alefcl.xdsml.design project.

It extends the fr.inria.glose.fcl.design project (see Section 2.2.2) by adding new layers.

A first layer Debug has been added to support interaction with the GEMOC debugger. It role is to provide:

  • breakpoint representation and actions (toggle breakpoint),

  • engine interaction, allowing to start an execution

  • execution stack highlighting (i.e. when clicking a step in the execution stack, it highlights the step target model elements)

A second layer Animation has been added to support domain specific (FCL) animation directly on top of the model diagrams.

As the graphical representation for FCL is composed of 2 diagrams (Mode and DataFlow), the new layers Debug and Animation must be specified in each of these diagrams.

2.5. Extra views

Preliminary version of the section, part of DP1.2

The demonstrator also provide a FCL specific view: FCL External Ports View.

This view is built as an eclipse UI view that registers to the execution as a GEMOC engine addon dedicated only to FCL.

This view is designed to show only the outer FunctionPorts of the running model.

It also allows to modify the values of these FunctionPorts.

The java code of this view is in the fr.inria.glose.fcl.alefcl.xdsml project.

2.6. Deployment

The modeling environment can be deployed in two ways:

  • for development purpose, a runtime workbench is started from the language workbench. It allows to test and debug the language modeling environment.

  • for use by the final end user (i.e. the IconeGemocModel 16 Modeler), the projects can be packaged and installed directly in an Eclipse product (a.k.a. RCP or Rich Client Platform).

2.6.1. Packaging and Deployment in an Eclipse Product

In order to package the FCL language for final user, one of the easiest way is to prepare an update site and upload it on a web server. Then, the user will be able to install the FCL modeling environment in any Eclipse IDE. As the FCL language is based on EMF, Sirius, Xtext and GEMOC, the runtime of these framework must be installed too. If not present in the choosen IDE, they can be installed using updatesite too.

The GEMOC Studio (http://gemoc.org/download.html) is the preferred IDE as it already contains all the prerequisite frameworks.

In the demonstrator, building this updatesite is done via the maven script of the releng/fr.inria.glose.fcl.updatesite project.

The resulting repository folder can then be communicated to the user (typically via a web URL) so he can use the HelpInstall New Software…​ menu of his Eclipse IDE as in Figure 13.

eclipse software install win10 v1
Figure 13. Install features from update site

In Figure 13, type the following URL as update site location to get the latest version of the FCL language: https://glose.gitlabpages.inria.fr/flight-controller-language-demonstrator/updatesite/latest/

The update site can also be advertised via the Eclipse Market Place (https://marketplace.eclipse.org/). This is a central place where developers can publish their contribution to the Eclipse ecosystem that is very convenient for end users.

Thanks to Eclipse RCP (Rich client Platform) technology, it is possible to create an IDE containing only the tools required for an activity. For example, as the IconeGemocModel 16 Modeler can have an IDE with only the runtime and not the developer version for Java, Xtext, Sirius,…​

2.6.2. Runtime Deployment

If you have imported all the source projects of the FCL language into an Eclipse IDE, to deploy it in a runtime workbench the IconeGemocLanguage 16Language engineer must do:

  • RunRun configurations…​ → Right click on Eclipse Application → new configuration

This creates a launch configuration (see [new-runtime-workbench-launchconf]) that runs a new Eclipse similar to the current one but also that also includes the plugins under development in the workspace.

RuntimeWorkbench-RunConfiguration-Screenshot.png

RuntimeWorkbench RunConfiguration Screenshot
Figure 14. Create new runtime configuration for a modeling workbench

3. Scenario #2: Simulating and Debugging a Behavioral Model

This scenario demonstrates the ability of the GEMOC Studio to provide a full-fledged modeling and execution environment, that supports a language user (i.e., a domain expert, being a systems engineer in the case of the chosen use case) in the design and early validation & verification of a complex system. This scenario shows how to edit a model, and execute it such as the language user can interact with it to inject external stimuli through the event manager and drive the execution accordingly. In addition, the scenarios demonstrates the various facilities provided to help manually debugging the model (wrt. to an expected behavior), such as breakpoint definition and time traveling (forward and backward).

3.1. Model Edition

The Modeling workbench designed in Chapter 2 offers several model editors.

3.1.1. Textual Editor in Action

The smallest valid FCL model would be similar to Listing smallest FCL model. With a single mode and a main function that does nothing.

Listing smallest FCL model
FlightControllerModel SmallestController {  (1)
    mainModeStateMachine {
        mode ManualAcrobaticFlight {}       (2)
        initialMode ManualAcrobaticFlight   (3)
    }
    mainFunction {                          (4)
    }
}
1 name of the model
2 definition of a mode
3 definition of the initial mode
4 main function

A slightly more complex example would look like Listing Small FCL model with one main function. It defines

Listing Small FCL model with one main function
FlightControllerModel SmallController {
    mainModeStateMachine {
        mode ManualAcrobaticFlight {}
        initialMode ManualAcrobaticFlight
    }
    mainFunction {
        ports {
            dataPort in i : integer defaultValue 0  (1)
            dataPort out o : integer
        }
        action {
            o := (i * 10)                           (2)
        }
    }
    dataTypes {
        IntegerValueType integer {}                 (3)
    }
}
1 the main function exposes an input port i and an output port o
2 the action indicates how to compute o
3 the model defines an integer type

And a minimal system that defines several modes will look like Listing Minimal FCL model with modes.

Listing Minimal FCL model with modes
FlightControllerModel minimalController {
    mainModeStateMachine {
        mode MultBy10 {
            enabledFunctions (X10)          (1)
        }
        mode MultBy100 {
            enabledFunctions (X10)          (1)
        }
        final mode Stop {}                  (2)
        initialMode MultBy10

        transition MultBy10 -> MultBy100 { when buttonPressed } (3)
        transition MultBy100 -> Stop { when buttonPressed }
    }
    mainFunction {
        ports {
            dataPort in i : integer defaultValue 0
            dataPort out o : integer
        }
        dataFlow {                          (4)
            function X10 {
                ports {
                    dataPort in ix10 : integer defaultValue 0
                    dataPort out ox10 : integer
                }
                action {
                    ox10 := (ix10 * 10)     (5)
                }
            }
            function X100 {
                ports {
                    dataPort in ix100 : integer defaultValue 0
                    dataPort out ox100 : integer
                }
                action {
                    ox100 := (ix100 * 100)  (5)
                }
            }
            connect X10.ox10 <-> o          (6)
            connect X100.ox100 <-> o
            connect i <-> X10.ix10
            connect i <-> X100.ix100
        }
    }
    events {
        external buttonPressed              (7)
    }
    dataTypes {
        IntegerValueType integer {}
    }
}
1 each mode indicates which sub function is active or not.
2 some mode may be final to end the execution.
3 transitions between modes. (can be triggered via events and/or guard on data).
4 the main function declares two sub functions (in this case they are exclusive).
5 each sub function declares an action indicates how to compute its output.
6 mapping between ports (in this case between child functions to parent functions, but this can also be between two children functions).
7 an event that can be raised (either internally from an action or from the system environment).
DemoController in XTeditor statemachine Screenshot
Figure 15. Xtext editor opened on the demo controller (mode state machine part)

3.1.2. Graphical Editor in Action

Opening the Sirius representations of the minimal controller Listing Minimal FCL model with modes provide the diagrams of Figure 16. A popup menu on every element or using the palette Open in textual editor allows to highlight the text of element in Xtext. The palette of the Function diagram also offers to create new elements (New Functions or new Ports) and to create a connection between ports.

MinimalController Sirius editor Screenshot
Figure 16. Small FCL model opened with the graphical editor.

More complex diagram would look like Figure 1 and Figure 2 which are overview of the models that will be released as part of the Example Flight Control Language models of DP3.1. They show the graphical editor diagrams applied to a Quadcopter controller.

3.1.3. Static Validation

The validation rules added in [sec-fcl-static-validation] are reported in the Modeling workbench in several ways:

  • directly in the editors using colored icons; (hovering over the element shows a tooltip with an explanatory text.)

  • in a summary in the Problem view

Figure 17 shows the type checker indicating a type error while trying to assign a Float in an Integer variable of FCL (the type JoystickInput is an Integer). Using a Right clickValidate diagram in the Sirius editor will also highlight the problem in the graphical editor. Each markers of both editor (textual and graphical) is reported in the Problems view.

TypeMismatch error Screenshot
Figure 17. Type mismatch error in the editors and Problems view

3.1.4. Basic Tree Editor in Action

As seen in Section 2.1.2, a simple tree editor is also available. since we haven’t generated the dedicated java code for it, the IconeGemocModel 16 Modeler can open a fcl file using a Right click on the .fcl fileOpen WithOtherSample Reflective Ecore Model Editor

Figure 18 shows this tree editor on the basic quadcopter controller.

DemoController in TreeEditor Screenshot
Figure 18. DemoController model in tree editor screenshot

3.2. Debugger in Action

Preliminary version of the section, part of DP1.2

Figure 19 shows the toggle breakpoint action and some elements with the breakpoint icon that is provided by the Debug layer.

DemoController toggle breakpoint DataFlow statemachine Screenshot
Figure 19. Toggle breakpoint in action on the DataFlow diagram.

3.3. Animation in Action

DemoController statemachine run Screenshot
Figure 20. Demo controller model running and paused after having changed mode.

Figure 21 shows the the demo controller model running and paused having evaluated all functions one time. The FCL External Ports view displays the current value of the port located on the MasterFunction (i.e. the outer function)

DemoController dataflow 1 big step run Screenshot
Figure 21. Demo controller model running and paused after having evaluated all functions one time.

Figure 22 shows how a double click (or right click → popup menu → Change Value) in the FCL External Ports view allows to changes its value.

DemoController dataflow 1 big step change value Screenshot
Figure 22. Demo controller changing value using the ExternalPorts view.

Figure 23 shows the Demo controller paused after an update of the global clock. The Multidimensional timeline shows the changes on the current value. The value is also visible in the Variables view and the Sirius diagram.

DemoController after clock update timeline Screenshot
Figure 23. Demo controller paused after an update of the global clock.