Les démons


o Les démons sur les descripteurs des entités
o Les démons sur la structure des entités
Un démon permet de spécifier des actions qui doivent être déclenchées lors de certains événements sur les entités : plus précisément des changements de structure (le démon est alors une instance de StructureMonitor), ou bien des accès ou changement de valeurs de descripteurs (instance de DescValueMonitor). L'action à déclencher est décrite dans le démon lui-même, puis le démon est attaché à l'entité (cas StructureMonitor) ou à un de ses descripteurs (cas DescValueMonitor). Ces deux classes sont présentées dans les liens de la liste en haut de page.

Un exemple typique de démon est la surveillance du franchissement d'un seuil lors par la valeur d'une variable d'état. Ce franchissement est peut-être le déclencheur d'une action corrective ou d'accompagnement. Sans le mécanisme de démon, le codage de cette situation est le suivant :

int main(...) {
  ...
  Entity* pE = GetNamedEntity(CUVE, "cuve1");
  float val;
  // forme 1
  val = pE->GetFloatVarValue(NIVEAU_COURRANT);
  if(val > (float)20.) {
    // une action corrective ou d'accompagnement
  }
  // forme 2 alternative équivalente
  val = gMyFloatVarValue(pE);
  ...
}

float gMyFloatVarValue(Entity* pE) {
  float val = pE->GetFloatVarValue(NIVEAU_COURRANT);
  if(val > (float)20.) {
    // une action corrective ou d'accompagnement
  }
  return val;
}

Le mécanisme permet de ne pas avoir à compléter, dans le code de l'application, chacun des appels aux méthodes standard, par exemple Get...Value(), par un morceau de code décrivant les actions associées (forme 1 ci-dessus), ni à les récrire en y intégrant l'action associée (forme 2). En d'autres termes, les méthodes standard conservent leur sémantique, mais, au cours d'une simulation, certaines de leurs invocations peuvent générer des effets de bord et les autres non.

Les étapes de l'installation d'un démon

Les éléments de code (et les étapes du codage) qui permettent le déclenchement automatique d'actions sont les suivants, en prenant l'exemple de l'accès à la valeur d'un descripteur flottant. Noter d'ores et déjà que ces fonctions sont les corps d'instances de la sous-classe MonitorMethod, et que ces instances sont descripteurs fonctionnels des classes DescValueMonitor et StructureMonitor.

  1. La déclaration du démon. Par exemple :
     
    // normalement dans UserMonitor.h :
    // déclaration 'extern' pour accessibilité dans les autres fichiers
    // (constructeurs des entités, notamment)
    extern FloatDescValueMonitor* CurrentLevelMonitor;
    
    // normalement dans UserMonitor.cc :
    FloatDescValueMonitor* CurrentLevelMonitor;
    

  2. La spécification et l'instanciation du démon. Il s'agit de doter le démon des fonctions qui seront déclenchées lors des événements d'accès ou de modification de valeurs. Il y a là trois sous-étapes :

    1. La définition des fonctions. Par exemple, l'utilisateur définit une fonction à déclencher lors de l'accès à la valeur du descripteur flottant NiveauCourant de toutes les instances de la classe Cuve :
      float CurrentLevel_whenGet(MonitorMethod* pM) {
        Descriptor* pVD = pM->GetDescriptorArgValue(MONITOREDDESCRIPTOR_ARGUMENT);
        //float oldValue = pM->GetFloatArgValue(CURRENTFLOATVALUE_ARGUMENT);
        float candidateValue = pM->GetFloatArgValue(CANDIDATEFLOATVALUE_ARGUMENT);
        if(candidateValue > (float)20.) {
          // une action corrective ou d'accompagnement
        }
        ...
        return candidateValue;
      }
      
      Le corps ci-dessus exploite les arguments prédéfinis (instances d'Argument) de la méthode pM, obligatoirement placée en unique argument de la fonction. Leur liste est précisée page 'La classe de base pour tous les types de descripteurs'.

    2. La création de l'instance de démon :
      CurrentLevelMonitor = new FloatDescValueMonitor("currentLevelMonitor");
      

    3. La liaison entre les fonctions "utilisateur" et les méthodes prédéfinies des sous-classes de DescValueMonitor. Par exemple :
        CurrentLevelMonitor->AssignWhenGetFloatMethod(CurrentLevel_whenGet);
      

    Quelle que soit la façon de les programmer, ces trois sous-étapes doivent être exécutées dans cet ordre. Cependant, elles peuvent être codées dans un seul et même fichier (UserMonitor.cc par exemple), sous réserve que la définition des fonctions (a) précède leur liaison (c). Les deux dernières sous-étapes (b et c) peuvent être incluses dans une fonction de spécification de l'ensemble des démons actifs pendant la simulation. Cette fonction peut être nommée App_DefineMonitors(), déclarée dans UserMonitor.h et réalisée dans UserMonitor.cc après la définition des fonctions.

    void App_DefineMonitors() {
      CurrentLevelMonitor = new FloatDescValueMonitor("currentLevelMonitor");
      CurrentLevelMonitor->AssignWhenGetFloatMethod(CurrentLevel_whenGet);
      ...
    };
    

    Normalement la fonction App_DefineMonitors est appelée en début du programme principal, pour que les démons soient actifs dès le début de la période simulée.

    Noter la recommandation de nommage : l'identificateur C++ du démon possède un suffixe 'Monitor', lequel est remplacé dans le nom des fonctions utilisateur par la chaîne '_whenSet' ou '_whenGet'.

  3. L'attachement du démon à une ou plusieurs entités, ou bien à un ou plusieurs descripteurs d'une ou plusieurs entités. Cet attachement est programmé dans les constructeurs des entités en question (on rappelle qu'à cet effet le démon a été déclaré extern). Par exemple, dans le constructeur de la classe Cuve, dont le descripteur NiveauCourant a pour symbole de classe NIVEAU_COURANT, on aura :
    Cuve::Cuve() {
      ...
      Descriptor* pD = new NiveauCourant();
      CurrentLevelMonitor->AttachToDescriptor(this, NIVEAU_COURANT);
      ...
    }
    

    L'attachement est ensuite réalisé dynamiquement lors de la création (par l'opérateur new) de toute instance de la classe en question. On insiste sur le fait qu'un démon déclaré, spécifié, instancié, équipé, reste inactif tant qu"il n'a pas été attaché à l'objet à surveiller.


This page was generated with the help of DOC++.