Exemple 1 : le toboggan



Le code ci-dessous permet de simuler la trajectoire d'une voiture sur une voie ayant le profil ci-dessus, entièrement dans un plan vertical. A tout instant, la position de la voiture est déterminée par : Lorsque la voiture atteint la limite gauche (X(t) = -1.2), elle ne va pas plus loin et sa vitesse est remise à 0. La vitesse est limitée à 0.07, dans les deux sens. Lorsque la voiture sort du secteur par la droite (X(t) > 0.5), la simulation est arrêtée. On mesure le temps que met la voiture pour atteindre ce but.

Fichier : UserEntity.cc
#include "UserEntity.h"

Voiture::Voiture() {
  SetMetaClass(VOITURE);
  //SetParentClassId(O_ENTITY); // pour mémoire
  AddInstanceToClass();

  AddComponentClassId (CONDUCTEUR);
  Descriptor* pD;
  pD = new InitialPosition();   AddConstantDescriptor (pD);
  pD = new InitialSpeed();      AddConstantDescriptor (pD);
  pD = new Speed();             AddVariableDescriptor (pD);
  pD = new State();             AddVariableDescriptor (pD);
  pD = new Position();          AddVariableDescriptor (pD);
  SpeedMonitor->AttachToDescriptor(this, SPEED);
};

Conducteur::Conducteur() {
  SetMetaClass(CONDUCTEUR);
  //SetParentclassId(O_ENTITY); // pour mémoire
  AddInstanceToClass();
  Descriptor* pD;
  pD = new Command();           AddVariableDescriptor (pD);
}; 

Constructeur d'instances de voitures.
Attachement de la métaclasse adéquate, celle
dont le symbole est celui de la classe.
La hiérarchie est définie sur la métaclasse.

Une voiture a un composant de type conducteur.
Construction et  attribution des descripteurs 
variables (position et vitesse). 
Construction et  attribution des descripteurs
constants (position et vitesse initiale).
La variable State sera mise à 0 dès que le 
but sera atteint (cf. UserContinuousProcess.cc).
Attachement d'un démon à la variable vitesse.


Constructeur d'instances de pilotes.



Construction et attribution d'un descripteur
variables (l'action de commande).

Fichier : UserTypes.h
enum App_ClassId {CONDUCTEUR = 201, VOITURE};
enum App_DescriptorId {COMMAND = 201, INITIAL_POSITION,
                INITIAL_SPEED, POSITION, SPEED, STATE};
enum ProcessId {COURSE = 1, SET_COMMAND}; 
Déclaration des symboles de classe utilisés
dans le programme.

Fichier : UserConstantDescriptor.cc
 
#include "UserConstantDescriptor.h"
#include "UserTypes.h"

InitialPosition::InitialPosition() {
	ClassName   ("initialPosition");
	ClassSymbol (INITIAL_POSITION);
	SetDefaultValue (-1.2);
};

InitialSpeed::InitialSpeed() {
	ClassName   ("initialSpeed");
	ClassSymbol (INITIAL_SPEED);
	SetDefaultValue (0.);
}; 




Constructeurs des classes de descripteurs 
constants.







Fichier : UserVariableDescriptor.cc
#include "UserVariableDescriptor.h"
#include "UserTypes.h"

Position::Position() {
	ClassName   ("position");
	ClassSymbol (POSITION);
	SetDefaultValue (-1.2);
	PosDomain* p = new PosDomain();
	SetDomain(p);
}; 
Speed::Speed() {
	ClassName   ("speed");
	ClassSymbol (SPEED);
	SetDefaultValue (0.);
}; 
State::State() {
	ClassName   ("state");
	ClassSymbol (STATE);
	SetDefaultValue (1);
}; 
Command::Command() {
	ClassName   ("command");
	ClassSymbol (COMMAND);
	SetDefaultValue (0);
};




Constructeurs des classes de descripteurs 
variables.
Affectation d'un domaine de valeurs.

Fichier : UserDescValueSpec.cc
#include "UserDescValueSpec.h"
#include "UserTypes.h"

PosDomain::PosDomain() : DescFloatValueSpec(IN){
	ClassName("posDomain");
	AddSubDomain(-1.3, 0.55);
	SortOnLowBound();
	ReduceToDisjunction();
};




Constructeur d'une classe de domaines de valeur.
On ne créera qu'une instance, pour le descripteur Position.

Fichier : UserFunction.cc
#include "UserFunction.h"

void InitSimulation() {
  pCurrentSim = new Simulation();
  //...
  App_AssignInstantiators();

  D_InstallMetaClasses();
  App_InstallMetaClasses();
  SetHierarchy();

  D_DefineMonitors();
  App_DefineMonitors();
}

void App_InstallMetaClasses() {
  App_MakeClassIdToCharTab();
  App_MakeDescIdToCharTab();
  MetaClass* pMC;
  pMC = CreateMetaClass(CONDUCTEUR, "conducteur");
  pMC->SetParentClassId(O_ENTITY);
  pMC = CreateMetaClass(VOITURE, "voiture");
  pMC->SetParentClassId(O_ENTITY);
}

void App_AssignInstantiators() {
  Assign_App_NewEntityFromStringFunction(
                        App_NewEntityFromString);
  Assign_App_NewProcessFromStringFunction(
                        App_NewProcessFromString);
  Assign_App_NewEventFromStringFunction(
                        App_NewEventFromString);
  Assign_App_NewEntitySpecFromStringFunction(
                        App_NewEntitySpecFromString);
  ///...
};

Entity* App_NewEntityFromString(char* nom) {
  if (!strcmp(nom, "conducteur")) return new Conducteur();
  if (!strcmp(nom, "voiture")) return new Voiture();
  return NULL;
};

Process* App_NewProcessFromString(char* nom) {
  if (!strcmp(nom, "course")) return new Course();
  if (!strcmp(nom, "setCommand")) return new SetCommand();
  return NULL;
}

Event* App_NewEventFromString(char* nom) {
  if (!strcmp(nom, "eventCommand")) return new EventCommand();
  return NULL;
}
 
Fonction d'initialisation de la simulation:
création de l'intance de Simulation ;

assignation des fonctions d'instanciation ;

installation des métaclasses BASIC DIESE ;
installation des métaclasses de l'application ;
établissement des liens hiérarchiques ;

installation des moniteurs de BASIC DIESE ;
installation des moniteurs de l'application ;


Réalisation d'une fonction déclarée dans DIESE :
initialisation des informations sur les sous-classes
d'entités.
Informations encapsulées dans des métaclasses.
Déclaration du lien hiérarchique.



Réalisation d'une fonction déclarée dans DIESE :
déclaration des fonctions d'instanciation d'objets
à partir de leur nom ou symbole de classe.






Fonction d'instanciation des entités à partir ...
... du nom de classe.





Fonction d'instanciation des processus à partir ...
... du nom de classe.




Fonction d'instanciation des événements à partir ...
... du nom de classe.

Fichier : UserFunction.cc (suite)
void App_ParseMainArguments (int argc, char* argv[]) {    
  if(argc < 6) {
    printf("Usage : main system_parameters simulation_parameters 
                      output_specifications structure directives");
    printf("        [-m(emory)] [-v(erbose)] [-T] \n");
    exit(0);
  };
  for (int i=6;i < argc;i++) {
    if(!strcmp(argv[i],"-v")) gTraceActionMode=TRACE_ACTION_ON;  
    if(!strcmp(argv[i],"-m")) gTraceNewDelMode=TRACE_NEWDEL_ON; 
    if(!strcmp(argv[i],"-T")) gTraceTableauMode=TRACE_TABLEAU_ON; 
  };
};  

Fonction d'interprétation de la ligne de commande.
 

Fichier : UserMonitor.h
#include "UserTypes.h"
extern DescValueMonitor* SpeedMonitor;

Déclaration d'un démon.

Fichier : UserMonitor.cc
#include "UserMonitor.h"

DescValueMonitor* SpeedMonitor;

float speed_whenSetFloat_Body(MonitorMethod* pM) {
  Descriptor* pVD = 
    pM->GetDescriptorArgValue(MONITOREDDESCRIPTOR_ARGUMENT);
  float oldValue = pM->GetFloatArgValue(CURRENTFLOATVALUE_ARGUMENT);
  float newValue = pM->GetFloatArgValue(CANDIDATEFLOATVALUE_ARGUMENT);
  if (((oldValue>0) && (newValue<=0)) 
      || ((oldValue<0) && (newValue>=0))) {
    Entity* pCar = pVD->DescribedEntity();
    Entity* pDriver = pCar->GetComponent(CONDUCTEUR);
    printf("\nChangement de direction en X = %5.3f (U = %d) date : %d", 
           pCar->GetFloatValue(POSITION), pDriver->GetIntValue(COMMAND), 
           pCurrentSim->Clock());
  };
  return newValue;
};

void App_DefineMonitors() {
  SpeedMonitor = new DescValueMonitor("speedMonitor");
  SpeedMonitor->AssignWhenSetFloatMethod(speed_whenSetFloat_Body);
}; 



Réservation d'un emplacement mémoire pour le 
démon.

Définition de la fonction qui sera déclenchée lors
de chaque modification de la vitesse de la voiture
(simple affichage). 









La nouvelle valeur est retournée
pour être affectée à la voiture.


Définition de la fonction de création du démon
avec attachement de la fonction ci-dessus.

Fichier : UserEvent.cc
#include "UserEvent.h"
             
bool eventCommand_generateNextEvent_Body(EventMethod* pM) {
  Event* pCurrentEvent = pM->DescribedEvent();
  Process* pCurrentProcess = pCurrentEvent->FirstProcess();
  Entity* pCar = (pCurrentProcess->ProcessedEntity())->GetSuperEntity();
  if (pCar->GetIntVarValue(STATE) == 1) {
    Event* pNewEvent = pCurrentEvent->RawCopy();
    pNewEvent->SetOccurrenceClockTime(pCurrentSim->Clock()+1);
    pCurrentEvent->SetNextEvent(pNewEvent);
    return true;
  }
  else
    return false;
};

EventCommand::EventCommand() {
  ClassName("eventCommand");
  ClearListProcessAction ();
  HasPostConsequence(FALSE);
  IsAutoGenerate(TRUE);
  SetGenerateNextEvent(eventCommand_generateNextEvent_Body);
};


Définition de l'événement de détermination de la
commande. Un tel événement doit intervenir à chaque
pas de temps. Sa seule conséquence sera de provoquer
le processus de détermination de la commande.

Définition de la fonction de génération d'un 
nouvel événement du même type, avec le même 
processus en jeu.

La génération n'est faite que si le processus de 
déplacement de la voiture ne doit pas être 
interrompu (State = 1). L'événement est programmé 
pour dans 1 pas de temps.


Constructeur de l'événement.




Affectation de la fonction d'autogénération
définie ci-dessus.

Fichier : UserDiscreteProcess.cc
#include "UserDiscreteProcess.h"

void setCommand_execProcess_Body(ProcessMethod* pM)
{
  DiscreteProcess* pP = pM->DescribedDiscreteProcess();
  Entity* pDriver = (Entity*)(pP->ProcessedEntity());
  Entity* pCar = (Entity*)(pDriver->GetSuperEntity());
  float X = pCar->GetFloatVarValue(POSITION);
  float V = pCar->GetFloatVarValue(SPEED);
  float frontiere = - 0.06*pow(X, 2) - 0.03*X + 0.01;
  if (V < frontiere)
    pDriver->SetIntVarValue(COMMAND, -1);
  else
    pDriver->SetIntVarValue(COMMAND, +1);
 };   

SetCommand::SetCommand() 
           : DiscreteProcess(SET_COMMAND, "setCommand") {
  SetExec(setCommand_execProcess_Body);
};


Définition de la fonction de détermination de 
la commande U(t) : on définit une frontière
dans le domaine X x V, en dessous de laquelle on 
freine et au dessus de laquelle on accélère. 
L'entité sur lequel porte le processus (renvoyé
par le message ProcessedEntity()) est déter-
miné dans main.cc, par le message 
ProcessedEntity(Entity*).






Constructeur des instances de processus de dé-
termination de U(t), auxquelles on attache la 
fonction ci-dessus. Un tel processus sera crée 
et exécuté à chaque pas de temps.

Fichier : UserContinuousProcess.cc
#include "UserContinuousProcess.h"
    
void course_initializeProcess_Body(ProcessMethod* pM) {
  ContinuousProcess* pP = pM->DescribedContinuousProcess();
  DescriptedEntity* pDO = pP->ProcessedEntity();
  pDO->SetFloatVarValue(POSITION, 
               pDO->GetFloatConstValue(INITIAL_POSITION));
  pDO->SetFloatVarValue(SPEED, 
               pDO->GetFloatConstValue(INITIAL_SPEED));
};    
    
void course_stopProcess_Body(ProcessMethod* pM) {
  ContinuousProcess* pP = pM->DescribedContinuousProcess();
  ((ContinuousProcess*)pP)->ToBeStopped(TRUE); 
  DescriptedEntity* pCar = pP->ProcessedEntity();
  printf("\n\n ==> Position finale : X = %5.3f, vitesse = %5.3f\n", 
         pCar->GetFloatValue(POSITION),pCar->GetFloatValue(SPEED));
  printf(" temps simulé = %d", pCurrentSim->Clock()); 
};    
   
void course_goOneStepForwardProcess_Body(ProcessMethod* pM) {
  ContinuousProcess* pP = pM->DescribedContinuousProcess();
  DescriptedEntity* pCar = pP->ProcessedEntity();
  Entity* pDriver = (Entity*)(pCar->GetComponent(CONDUCTEUR));
  float Xt = pCar->GetFloatVarValue(POSITION);
  float Vt = pCar->GetFloatVarValue(SPEED);
  int   Ut = pDriver->GetIntVarValue(COMMAND);
  float Vtp1 = Vt+0.001*Ut-0.0025*cos(3*Xt);
  float Xtp1 = Xt+Vtp1;
  if (Xtp1 > 0.5) {
    pCar->SetFloatVarValue(POSITION, Xtp1);
    pCar->SetFloatVarValue(SPEED, Vtp1);
    pCar->SetIntVarValue(STATE, 0);
    pP->ExecVoidMethod(STOP_PROCESS);
  }
  else if (Xtp1 < -1.2) {
    Xtp1 = -1.2;
    Vtp1 =  0.0;
  };
  if (Vtp1 > 0.07) { Vtp1 = 0.07; }
  else if (Vtp1 < -0.07) { Vtp1 = -0.07; };
  pCar->SetFloatVarValue(POSITION, Xtp1);
  pCar->SetFloatVarValue(SPEED, Vtp1);
};   
    
Course::Course() : ContinuousProcess(COURSE, "course") {
  Pas(1);
  SetInitialize(course_initializeProcess_Body);
  SetGoOneStepForward(course_goOneStepForwardProcess_Body);
  SetStop(course_stopProcess_Body);
};

Définition de la fonction d'initialisation
de la trajectoire. Les variables position et 
vitesse prennent la valeur des paramètres
position initiale et vitesse initiale.





Définition de la fonction d'arrêt du dé-
placement de la voiture. La méthode ToBeStopped 
fait partie de la bibliothèque DIESE.  






Définition de la fonction de changement
d'état de la voiture pour un pas de temps.








Calcul de la nouvelle vitesse V(t+1) et de la 
nouvelle position X(t+1).
Si le but est atteint, la variable State est 
mise à 0, et le processus est arrêté.
Si la limite gauche est atteinte, la vitesse est 
mise à 0. Sinon, elle est éventuellement 
limitée à 0.07.




Les valeurs terminales sont attribuées aux
descripteurs Position et Vitesse de la voiture.


Constructeur d'instances du processus de dé-
placement d'une voiture.


Affectation des fonctions précédemment définies.

Fichier paramètres du système: sim0.par
// parametres du système toboggan
ENTITE voiture position_initiale <- -0.5;
ENTITE voiture vitesse_initiale <- 0.0;
Déclaration de deux paramètres à valeur flottante.

Fichier des paramètres de la simulation : sim0.sim
INITIALISATION SIMULATION
     UNITE_TPS SECOND;
     NB_UNITE_TPS 1;
     DATE_DEBUT 1 JAN 2000   12 0 0;
     DATE_FIN   1 JAN 2000   12 4 0;
     OUT_DIR "/home/durand/DIESE/KBS/bdiese/Toboggan/out/";
;

Définition de l'unité de temps : toutes les indications de
durée et de valeurs d'horloge sont implicitement en secondes.

Le trafic est simulé 4 minutes à partir de midi.
Les sorties (voir le fichier outspec), seront dirigées dans
le répertoire désigné ici.

Fichier des spécifications de sortie : sim0.osp
SAVE DESCRIPTOR voiture ALL f_PS.txt NEW CLOCK 
                position 2 speed 3;
On demande la trace de tous les changements de valeur de
la position et de la vitesse de la voiture.

Fichier de de la structure du système : structure
//TRACE PARSING;
+ I voiture pCar,
    initialPosition = < pr >< voiture position_initiale >;
    initialSpeed  = < pr >< voiture vitesse_initiale >;
    state = 1;

    + C conducteur,
    ;
;
//----------------------------------------
+ P course pPC
    ENTITE_CIBLE = < I >< voiture, >; 
    INIT_EVT DATE_OCCUR = 0; PRIORITE = 5;
    ; 
    PRCD_EVT DATE_OCCUR = 0; PRIORITE = 10;
    ;
;
+ P setCommand pPF
    ENTITE_CIBLE = < I >; 
;
//-----------------------------------------
+ V eventCommand pV
    DATE_OCCUR = 0;  PRIORITE = 9;
    PROCESS_ACTION = {< P >< ,pPF > (EXEC)}; 
;
 
Création d'une voiture.
  - affectation de valeurs aux descripteurs à partir
    des paramètres


  - création d'un pilote pour la voiture créée et 
    affectation comme composant de la voiture


Création du processus de déplacement de la voiture,
avec un pas de 1 unité de temps.
Attachement du processus à la voiture.
Création de des événements d'initialisation et de 
poursuite du processus.


Création du processus ponctuel de détermination
de la commande, et attachement au pilote. 


Création du premier événement de détermination 
de la commande, en t=0. La priorité 9 le place entre
ceux d'initialisation (priorité 0)et de première avancée 
(priorité 10) du processus de dépacement de la voiture.

Fichier des directives de la simulation : sim0.dir
CHRONO INIT;
CHRONO START;
RUN;          
MESSAGE ENDL;
CHRONO PAUSE;
CHRONO DISPLAY ENDL "duree execution : "; MESSAGE ENDL;
CHRONO CLOSE;

DISPLAY OUTPUTSPEC ALL;

DELETE ALL;
Démarrage du chronomètre.

Lancement de la simulation.

Affichage de la durée du calcul.


Sauvegarde des informations demandées dans le fichier outspec.

Récupération de la mémoire non encore désallouée.

Fichier : main.cc
#include 

##include "UserTypes.h"

main (int argc, char* argv[]) { 
  App_ParseMainArguments (argc, argv);
  ParseParameterFile(argv[1]);
  InitSimulation();
  ParseSimulationFile(argv[2]);
  ParseOutputSpecFile(argv[3]);
  ParseStructureFile(argv[4]);
  ParseDirectiveFile(argv[5]);
}
  




appel : main sim0.par sim0.sim sim0.osp sim0.str sim0.dir


Lecture et exploitation des fichiers en entrée.






Trace d'une exécution :
ossau% main sim0.par sim0.sim sim0.osp sim0.str sim0.dir

 Changement de direction en X = -0.82319 (U = 1) date : 34
 Changement de direction en X = 0.235503 (U = 1) date : 86
 Changement de direction en X = -1.2 (U = -1) date : 119

 ==> Position finale : X = 0.536858, vitesse = 0.0499571
 temps simulé =  158

duree execution :  0''019
ossau%  

Extrait du fichier de sauvegarde : f_PS.txt
#voiture  clock	Positio	Speed

pCar	0	-0.50	-0.001
pCar	1	-0.50	-0.002
pCar	2	-0.51	-0.003
pCar	3	-0.51	-0.005
pCar	4	-0.52	-0.006
pCar	5	-0.52	-0.007
...
...
pCar    32      -0.82   -0.006
pCar    33      -0.82   -0.003
pCar    34      -0.82   0.000
pCar    35      -0.82   0.003
...
...
pCar    84      0.23    0.002
pCar    85      0.24    0.001
pCar    86      0.24    -0.000
pCar    87      0.23    -0.001
...
...
pCar    117     -1.09   -0.060
pCar    118     -1.15   -0.059
pCar    119     -1.20   0.000
pCar    120     -1.20   0.003
...
...
pCar	156	0.44	0.049
pCar	157	0.49	0.049
pCar	158	0.54	0.050