• Aucun résultat trouvé

Avant de parler de l’implémentation des objets dynamiques, il est intéressant d’examiner l’implémentation des objets statiques qui vont imposer des contraintes lors du développement. Ces objets sont contenus dans le corps du FTM qui contient la machine d’état globale du mécanisme. Nous nous intéresserons d’abord au fonc- tionnement nominal du FTM. Pour cela, nous regarderons en premier l’Ordonnan- ceur puis le Gestionnaire d’Objet.

3.1.1 Le Gestionnaire d’objet

Le rôle du Gestionnaire d’objet est multiple. Il doit charger en mémoire les bi- bliothèques contenant les plugins, instancier dynamiquement les plugins, récupérer leur méthode par l’intermédiaire des pointeurs de fonction, construire la table d’or- chestration et la transmettre à l’Ordonnanceur. Pour valider l’implémentation de l’approche par actions ordonnançables, nous avons implémenté les trois FTM qui sont le PBR, le TR, le LFR et leur composition. Ces trois mécanismes suffisent à effectuer les différentes transitions (reconfiguration, adaptation, composition) pour valider la mise en œuvre de l’AFT. Pour des raisons de simplification, les tables d’orchestration sont construites manuellement et non automatiquement depuis un fichier. Nous n’avons pas effectué le travail d’interprétation nécessaire pour transfor- mer le fichier en table d’orchestration, ce qui ne pose pas de difficulté particulière. 3.1.2 Création des plugins

La création de plugins passe d’abord par le chargement en mémoire de la biblio- thèque les contenant. Pour rappel, ces bibliothèques contiennent les classes mères et filles qui correspondent aux catégories et sous-catégories de nos actions de SdF. Pour charger dynamiquement les bibliothèques, nous créons autant d’objets Class- Loader que de classes génériques d’action de SdF que nous utilisons comme montré

sur la figure du listing 4.7 de la section 2.4 de ce chapitre (ligne 5). psm_loader =

new pluginlib::ClassLoader<gestEtat_base::GestEtat>("GestionEtat",

"gestEtat_base::GestEtat");

֒→

Cet exemple illustre le chargement de la bibliothèque Gestion d’État qui se situe dans le package GestionEtat et dont la classe mère est gestEtat_base : :GestEtat. L’objet psm_loader créé va permettre d’instancier un objet dynamique à l’aide de la commande createInstance (ligne 8 de la même figure).

Une fois toutes les bibliothèques nécessaires chargées en mémoire, nous instan- cions dynamiquement nos plugins. Pour ce faire, nous utilisons les loaders précé- demment créés avec la méthode.

boost::shared_ptr psm =

psm_loader->createInstance("gestEtat_plugin::GestEtat_GetSet_Ckpt")

֒→

psm->init();

Ce code illustre la création et l’initialisation d’un plugin de Gestion d’État qui utilise une technique de type Getter/Setter avec transmission de l’état par message de checkpoint. C’est l’implémentation de l’objet dynamique de gestion d’état utilisé dans un PBR. Nous effectuons la même démarche pour tous les plugins nécessaires à la création du graphe d’actions. Nous allons maintenant étudier la construction de la table d’orchestration.

3.1.3 De la table d’orchestration à la map

Nous avons vu dans la section 1.3.2 que la table d’orchestration fonctionne comme un dictionnaire. Nous utilisons des clefs pour représenter les actions de SdF et qu’à ces clefs sont associées des définitions. La définition contient le pointeur de fonction vers la méthode d’action, la clef suivante si le résultat de la méthode est Vrai, la clef suivante si le résultat est Faux et la clef de traitement d’erreur.

ClefAction⇒ {∗M ´ethode, ClefV rai, ClefF aux, ClefErr}

Il existe dans la bibliothèque standard C++ un objet permettant de réaliser notre table d’orchestration. Cet objet s’appelle une map et permet d’associer à une clef (type string) n’importe qu’elle donnée :

std::unordered_map<std::string,S>

Ainsi, nous avons créé une structure présentée sur la figure de Listing 4.8 per- mettant de regrouper les pointeurs de fonction ainsi que les différentes clefs associées au résultat Vrai ou Faux et au traitement d’erreur. Le typedef définit l’interface des méthodes des plugins. Cela permet de définir le type de nos méthodes utilisées. Nous allons expliquer, dans l’implémentation de l’Ordonnanceur, le choix de cette interface pour l’implémentation des méthodes d’action.

3. Implémentation des FTM sous ROS 133

1 #include <functional>

2

3 typedef std::function<int(void*,void*)> sche_function; 4 5 struct S{ 6 sche_function funct; 7 std::string next0; 8 std::string next1; 9 std::string err; 10 };

Listing 4.8 – Structure représentant la définition associées à une clef d’action Le Gestionnaire va remplir chaque ligne de la map en associant à une même clef cette structure. Pour ce faire, il va récupérer les pointeurs de fonction des méthodes d’action des plugins. Nous avons donc pour chaque bibliothèque contenant des plugins un accesseur qui récupère la méthode d’action du plugin. De plus, il va ajouter à la map la clef INIT à laquelle il associe juste à la clef suivantVrai, la clef de l’action suivante. La clef END est ajoutée comme clef suivantVrai pour la dernière action. Une fois la map remplie avec l’algorithme du FTM choisi, le Gestionnaire d’objets la transmet à l’Ordonnanceur pour entamer le fonctionnement du FTM. 3.1.4 L’Ordonnanceur

Avant d’entrer dans les détails de l’implémentation de l’Ordonnanceur, nous nous intéressons à l’implémentation de l’algorithme d’ordonnancement qu’il suit. Nous avons défini plus tôt une table d’orchestration dans laquelle une clef est asso- ciée à un ensemble contenant la méthode à appeler, et les clefs suivantes en fonction du résultat retourné par la méthode (Vrai, Faux, Erreur). Cette table est construite sous forme de map dans le Gestionnaire d’objets.

Pour que l’Ordonnanceur puisse appeler les méthodes d’objets différents, il faut appliquer une contrainte forte. L’interface de la méthode doit être générique (dé- finition du type de retour et des paramètres). Ainsi, l’Ordonnanceur peut appeler n’importe quelle méthode d’action par l’intermédiaire d’un pointeur de fonction.

Occupons nous d’abord de la première contrainte. Les méthodes appelées sont issues d’objets différents et doivent donc être standardisée pour que l’Ordonnanceur puissent avoir un algorithme générique. De plus, la méthode doit renvoyer une valeur pour définir la méthode suivante à appeler. Ensuite, les paramètres doivent être génériques. Nous avons décidé de faire en sorte que les méthodes d’actions aient comme type de retour un entier et comme paramètre deux void*. De plus, comme le graphe d’actions est séquentiel, nous avons décidé que la valeur de sortie (out) d’un objet sera la valeur d’entrée (in) de l’objet suivant.

Le choix de ce type pour les paramètres vient d’une volonté d’avoir une interface générique alors que le type des paramètres diffère en fonction des méthodes choisies. En effet, cela nous permet de transmettre des données différentes aux objets sans se préoccuper pas du type des paramètres. C’est une solution acceptable car nous passons par des variables intermédiaires dans les méthodes pour ne pas manipuler directement les paramètres. De plus, la taille de ces paramètres est allouée à l’initia- lisation dans le conteneur du FTM et transmis à l’Ordonnanceur pour qu’il puisse appeler les méthodes d’actions avec ces paramètres.

Pour simplifier l’utilisation de cette interface, nous avons utilisé le template de classe std::function qui permet de manipuler une cible appelable (ici une fonction) en définissant par avance son type et ses paramètres. Cela permet de créer un alias (typedef ) pour définir des méthodes génériques. Nous verrons dans la section suivante comment nous avons récupéré ces méthodes à partir des plugins

La figure du listing 4.9 explicite les différentes méthodes contenues dans l’Or- donnanceur. Nous avons une méthode d’initialisation (ligne 3) avec récupération des adresses contenant les paramètres qui seront transmis aux différentes fonctions, une méthode d’ordonnancement (ligne 10), une méthode de récupération de la map (ligne 23), une méthode de récupération de la première méthode à appeler (ligne 28) et enfin la méthode de récupération de la méthode suivante à appeler en fonc- tion de la condition de retour (ligne 34). De plus nous avons un constructeur et un destructeur qui ne sont pas représentés ici car ils ne présentent pas de spécificités particulières.

A ce point, nous connaissons les différentes méthodes incluses dans l’Ordonnan- ceur et le Gestionnaire d’objet. Nous allons maintenant nous intéresser aux objets dynamiques et aux méthodes liées à cette nouvelle approche.