====== Tutoriel : les principes de génie logiciel ======
Cette partie du tutoriel illustre, au regard des principes de génie logiciel, les points particuliers du langage //nomo// dans le cadre d'un projet.
L'exemple consiste à réaliser dans un premier temps un agent devant trouver une bille, la prendre puis de tenter de la déposer sur une dalle bleue. L'agent explorera le monde de manière aléatoire.
Puis, dans un second temps, l'objet de l'exemple consistera à dupliquer et à gérer quatre agents devant réaliser cette tâche comme l'illustre par exemple la figure ci-dessous.
{{ :tutorial:tutorial_3_map.png?400 |}}
===== Définition d'un agent =====
La définition d'un agent se traduit par un programme //agent// s'appuyant sur un modèle //agent// qui se base sur le modèle prédéfini du [[nomosdk:bancs_essais#le_modele_de_nomo_de_base|monde de dalles]] //worldsquare//.
Dans cet exemple, la conception de l'agent s’effectuera en deux étapes :
- la définition de la logique des états internes,
- la définition des actions en fonction de ces états.
==== Formalisation ====
La première étape consiste à formaliser le comportement de l'agent sous la forme d'un [[nomosdk:formalisme#les_automates_a_etats_finis|automate]] :
{{ :tutorial:tutorial_3_automaton_1.png?500 |}}
L'automate se traduit par la création d'un programme //controller// contenant sa description comme suit :
Le programme //controller// sera un [[doc:langage_nomo#les_sous-programmes|sous-programme]] du programme //agent//. Chaque agent devant contenir son propre système de contrôle, l'automate correspond à une nouvelle instance.
Pour utiliser les types //state// et //event// générés par le sous programme, il faut [[doc:langage_nomo#les_modeles|importer]] le modèle de //controller// contenu par le programme //controller// dans le programme //agent//. Ces différentes opérations s'écrivent comme suit :
...
...
...
Le schéma ci-dessous illustre les relations de dépendance :
{{ :tutorial:tutorial_3_tree1.png?300 |}}
Dont voici la légende :
{{:tutorial:tutorial_3_tree_legend.png?47 |}} Programme (//new//)\\ Programme hérité (//inherit//)\\ Modèle (//new//)\\ Modèle importé (//import//)\\ Modèle hérité (//inherit//)\\ Représentation de l'arborescence\\ Spécification du numéro d'instance\\ L'original vers la copie
==== Définition des actions et des évènements ====
La seconde étape consiste à définir les actions au cours de ces états et le déclenchement des évènements.
Pour les états //search_sphere// et //search_location//, les actions peuvent se décomposer en deux phases :
- une phase d'exploration,
- une phase d'évaluation.
Il restera enfin la gestion de l'état //terminate//.
=== L'exploration ===
Pour les états //search_sphere// et //search_location//, la première phase d'action est identique de manière aléatoire, soit de tourner à gauche, soit de tourner à droite, soit
La première phase d'action repose sur le déclenchement de deux règles successives : une règle de perception de type //random// suivie d'une règle de commande de type //motor//.
Cette première phase d'action se déclenche à la transition des états sauf pour l'état //terminate//. Les règles de perceptions auront une prémisse //state// permissive sur le contenu mais stricte sur l'étiquette temporelle et une prémisse inhibitrice sur l'état //terminate//.
Le choix entre le trois commandes repose sur la perception de la variable aléatoire de l'agent dont la valeur sera considérée soit petite, soit moyenne, soit grande. Au modèle s'ajoute alors au type //random// trois items : //small//, //middle//, //large//.
La [[nomosdk:macro#la_construction_de_regles_a_partir_d_intervalles|macro de discrétisation]] permet de définir les règles perceptives de type //random// de la manière suivante :
Au déclenchement d'une règle de perception, //random// doit impliquer le déclenchement de la règle de commande motrice associée, soit la [[nomosdk:macro#les_patrons_de_regles|macro de patron]] suivante :
command, output, random
advance, 2, middle
turn_left, 0, small
turn_right, 1, large
=== L'évaluation ===
Selon l'état //search_sphere// et l'état //search_location//, l'évaluation du résultat diffère un peu :
* Pour l'état //search_sphere//, l'agent a-t-il été déplacé et si oui l'agent a-il pris une sphère ?
* Pour l'état //search_location//, l'agent a-t-il été déplacé et si oui l'agent est-il sur une dalle bleue ?
L'échec du déplacement ou de la prise d'une sphère se traduit par la perception d'une résistance correspondant au type d'entrée apériodique //strength// soit la règle perceptive idoine :
L'entrée //hue// est périodique, à chaque pas, des messages entrés sont reçus par l'agent. Dans le modèle //agent//, il est prévu que l'agent puisse percevoir une teinte : //red//, //blue// et //green//. Mais la perception de ces messages n'est pertinente uniquement lorsque l'agent se trouve dans l'état //search_location// et qu'il vient d'avancer, soit la [[nomosdk:macro#la_construction_de_regles_a_partir_d_intervalles|macro de discrétisation]] suivante :
La tentative de capturer une sphère est également conditionnée à l'état de l'automate //controller// et à la réussite de l'avance soit :
À partir de ces différentes règles, la définition des évènements de l'automate //controller// peuvent s'exprimer de la manière suivante avec deux [[nomosdk:macro#les_formulations_condensees_de_regles|macros de formulation]] :
blue_hue : found
failed ! capture : found
failed + other_hue + turn_right + turn_left : no_found
=== Finalisation ===
La finalisation correspond à la transition vers l'état //terminate// de l'automate //controller//. L'action associée est celle de déposer la sphère contenue par l'agent soit :
A noter qu'aucune vérification de la réussite de l'action n'est effectuée. En effet, la présence d'une sphère empêcherait le dépôt mais cela sort du cadre de l'exemple.
===== Ajout d'un interrupteur =====
Dès le début, l'état //Search_Sphere// de l'automate //controller// l'état est déclenché.
Maitriser ce déclenchement revient à introduire un nouvel état avec deux événements représentant les transitions d'un interrupteur.
{{ :tutorial:tutorial_3_automaton_2.png?600 |}}
L'expression de l'automate devient :
L'interrupteur est modélisé par un type de conception appelé //switch// possédant deux items //on// et //off// défini dans le modèle //switch//.
Le déclenchement de l'interrupteur pouvant avoir sa propre logique de fonctionnement, il constituera un autre programme, ici la logique de fonctionnement est minimale :
Le modèle //switch// est hérité puisque le message doit être accessible par ailleurs.
Bien que l'item //off// ne soit pas exploité ici par souci d’exhaustivité, la définition des deux évènements de l'automate //controller// est rajoutée :
En anticipant, le fait que l'interrupteur puisse être commun à plusieurs agent, le programme //switch_on// est [[doc:langage_nomo#les_sous-programmes|hérité]] ainsi que le modèle //switch// :
...
...
Soit le schéma de dépendance suivant :
{{ :tutorial:tutorial_3_tree2.png?350 |}}
===== Définition d'un groupe d'agent =====
Le programme //agent// défini, il est possible de l'utiliser comme programme principal. Toutefois, dans le cadre de l'exemple, quatre agents doivent parcourir un monde de dalles, il faut alors définir un autre programme regroupant quatre instances du programme //agent// :
Le schéma de dépendance ci-dessous montre bien que chaque agent possède son propre interrupteur dont la valeur est bien distincte :
{{ :tutorial:tutorial_3_tree3.png?600 |}}
Pour qu'un seul interrupteur soit commun à tous les agents, il suffit d'en instancier un avec le modèle associé afin que la communication entre les agents et leur interrupteur puisse se faire.
Soit le schéma de dépendance ci-dessous :
{{ :tutorial:tutorial_3_tree5.png?710 |}}
Dans le cas où le programme //switch// est instancié mais pas le modèle, alors les agents n'auraient pu se servir des messages envoyés par l'interrupteur, comme le montre le schéma de dépendance ci-dessous, car les instances du modèles //switch// sont différentes :
{{ :tutorial:tutorial_3_tree6.png?710 |}}
Dans le cas où le modèle //switch// est instancié mais pas le programme, alors il y aurait eu une duplication du programme //switch// donc duplication de règles avec un contenu identique, comme le montre le schéma de dépendance ci-dessous :
{{ :tutorial:tutorial_3_tree4.png?680 |}}