Projet IDM 2017/18 : machines à états

Le but de ce projet est de transformer et exécuter des machines à états simplifiées comportant un mini-langage d'action.

Description du méta-modèle

Le méta-modèle de machines à états considéré est representé dans la figure ci-dessus. Ses éléments sont :

Un modèle de machine à états se base donc sur une instance de l'élément StateMachine. Cette instance est unique par modèle et sert de départ à la création et au référencement de tous les états et transitions du modèle. Cette unicité est assurée par une contrainte OCL. D'autres contraintes OCL sont nécessaires pour notamment spécifier que :

Il faut également assurer la cohérence des états actifs d'une machine à états : si un état composite est actif, alors un (et un seul) de ses sous-états est actif et si cet état est également un état composite, alors un (et un seul) de ses sous-états est actif et ainsi de suite jusqu'à aboutir à un état final non composite. A l'exception de ces états, tous les autres états de la machine à états sont non actifs.

Note : dans le méta-modèle Ecore du projet, les attributs _name des éléments Expression et Assignment ou autres méta-éléments associés n'ont pas de sémantique particulière. Ils servent juste à pouvoir identifier les instances de ces éléments via un nom textuel ou préciser leur contenu donné sous forme textuelle.

Exemples de modèles et des transformations attendues

Exécution d'une machine à états (sans gardes et opérations)

La figure ci-dessous présente un exemple de machine à états modélisant le fonctionnement d'un micro-onde. Les étapes représentent le traitement de différents événements pendant l'exécution du modèle (la dernière est la mise à plat de la machine à états dont il sera question plus bas).

L'exécution d'une machine à états consiste, à partir d'une suite d'événements, à suivre les transitions associées si elles correspondent aux événements et à des états actifs. Suivre une transition se fait en désactivant l'état source et en activant l'état cible (tout en conservant les cohérences d'activation de leurs hiérarchies d'états). Si cet état cible est un composite, alors on active son état initial, et cela de manière récursive au besoin.

Sur la figure, à la première étape, les états actifs sont Off et son état composite, c'est-à-dire Closed (seul l'état final est représenté en gris mais toute la hiérarchie allant de cet état à la machine à état globale sont actifs). Entre la première et la deuxième étape, l'événement traité est Power. Il correspond à la transition entre Off et Baking. Ainsi, Baking devient l'état actif à la place de Off. Entre la deuxième et la troisième étape, l'événement considéré est DoorOpen qui fait passer dans l'état actif Paused de Open. De manière précise, il existe en fait deux transitions qui peuvent être suivies ici, toutes deux correspondant à l'événement DoorOpen : de Baking vers Paused et de Closed vers Open. Dans ce contexte, quand plusieurs transitions sont possibles, on choisit de suivre celle qui part de l'état le plus en bas de la hiérarchie des états actifs, donc ici partant de Baking et non pas de Closed.

Exemple d'une machine à états avec gardes et opérations

La figure ci-dessus représente une machine à états de la vitesse d'une voiture. La vitesse varie entre 0 et 100 km/h, par pas de 10 km/h. La machine à états possède deux état principaux : Arrêt et Marche, selon que la vitesse de la voiture est nulle ou pas. Deux événements sont gérés : accélerer et ralentir. L'état Marche contient 3 états internes permettant d'incrémenter et décrémenter la vitesse ainsi que de préciser que l'on est arrivé à la vitesse maximale. La machine à états possède deux variables : vitesse, de type entier, initialisé à 0, et maxAtteint, de type booléen, initialisé à false.

Syntaxe textuelle

La syntaxe textuelle à implémenter en XText devra respecter l'exemple ci-dessous. Il faudra être capable de définir l'intégralité de la structure de la machine à états (états et transitions) mais concernant le code, on ne gérera que de la simple affectation de variable avec une donnée. Autrement dit, il n'y a pas d'expression de calculs ni de gardes à gérer avec la syntaxe.


StateMachine Voiture {

   var vitesse : integer
   var maxAtteint : boolean := false

   events { "accelerer", "ralentir" }

   state Arret{
      do { vitesse := 0 }
   }

   state Marche {
      state MoinsVite {}
      state PlusVite {}
      state Maximum {
         do { vitesse := 100; maxAtteint := true }
      }
      init with PlusVite
   }

   init with Arret

   from Arret to Marche for "accelerer"
   from Marche to PlusVite for "accelerer"
   from Marche to MoinsVite for "ralentir"

}

Mise à plat d'une machine à état

Le passage de la troisième à la dernière étape de la figure du micro-onde correspond à la mise plat d'une machine à états hiérarchique. Le principe est qu'après la mise à plat, il n'y a plus aucun état composite. La machine à états mise à plat doit avoir exactement le même comportement que la précédente.

Dans l'exemple, on retrouve les quatre états finaux mais non imbriqués cette fois dans deux états composites. Les états Off ont été renommés pour ne pas avoir deux états du même nom. Les transitions ont ensuite été déplacées au besoin pour obtenir le même comportement (de Off vers Off ou de Baking vers Paused). Dans l'étape trois, l'état actif était Paused, c'est donc lui qui reste l'état actif de la machine à états mise à plat.

Travail attendu

Trois éléments sont attendus dans le projet :

  1. Complèter le moteur d'exécution Kermeta pour prendre en compte les gardes des transitions et exécuter les opérations associées aux états en mettant à jour le contenu des variables.
  2. Une transformation ATL réalisant la mise à plat d'une machine à états composite (sans prendre en compte les opérations associées aux états).
  3. Une syntaxe XText identique à l'exemple donné ci-dessus.

Ressources du projet

L'archive StateMachine.zip contient le projet Eclipse implémentant les éléments initiaux du projet :