FAQ

Un article de OpenMASKWiki.

Jump to: navigation, search

Q: Comment utiliser PsName dans un test de manière performante ?

Q: Comment identifier efficacement les événements et signaux ?

Q: Puis-je utiliser des static dans un objet simulé ?

Q: Pourquoi dois-je utiliser des héritiers de PsType pour échanger des données entre Objets de Simulation OpenMASK ? Ne serait-ce pas aussi simple avec des types classiques int, float, struct,...?

Q: J'ai un Objet de Simulation "A" qui doit mettre à jour une sortie dans un autre Objet "B" et cela ne fonctionne pas, OpenMASK me dit que l'Objet "B" n'est pas l'objet actif. Comment résoudre ce problème ?

Q: Dans un événement valué ou un output je dois transmettre plusieurs paramètres en même temps. Comment faire ?

Q: Existe-t-il des règles de codage pour développer des modules OpenMASK ?

Q: J'utilise rand() pour placer mes objets et pourtant ils apparaissent tous au même endroit. Pourquoi ?


Q: Comment utiliser PsName dans un test de manière performante ?

Fondamentalement PsName est un identifiant (nombre), même si l'utilisateur le voit sous forme de texte. C'est le PsNameServer qui stocke l'ensemble des textes et attribue à chacun un identifiant. Lorsqu'un PsName est créé, le PsNameServer compare le texte fourni à la création avec les textes qu'il possède déjà et retourne l'identifiant correspondant, s'il ne trouve pas le texte il le mémorise et retourne un nouvel identifiant. On sait que comparer deux nombres est très rapide, comparer deux textes est très lent. Aussi, lors de comparaisons de PsName il y a un problème de performance. Aussi, il est préférable d'écrire :-)

bool myOSO::processEvent( PsEvent *event )
{
  static PsName s_eventName( "theEvent" ) ;
  if( event->eventId == eventName )
  { 
    // Compare identifiers => fast
  }
}

que :-(

bool myOSO::processEvent( PsEvent *event )
{
  if( event->eventId == "theEvent" )
  { 
    // Compare strings => slow
  }
}

Dans le premier cas, s_eventName est créé une fois (déclaré static) puis les tests s'effectuent sur les identifiants. Dans le second cas, le texte correspondant à event->eventId est récupéré et les deux textes sont comparés, ce qui est nettement moins performant. Note : avec la version 3.3.0 (ou antérieure) il n'y a pas de comparateur entre un PsName et un texte, en revanche on peut créer un PsName à partir du texte. Aussi le compilateur crée un PsName temporaire puis compare les identifiants respectifs. Cela est catastrophique pour la performance.

retour


Q: Comment identifier efficacement les événements et signaux ?

Voici un modèle d'écriture pour l'identification d'un événement ou d'un signal. Chaque événement (ou signal) est identifié par un PsEventIdentifier. Dans la mesure où ce nom est fixe dans l'émetteur, il est recommandé de définir un membre static pour ne pas avoir à générer l'identifiant à chaque envoi d'événement.

// .h
class PsMySender: public PsSimulatedObject 
{
public:
  static PsEventIdentifier s_eventId ;
protected:
  PsName _eventProcess ;
...
} ;
//.cxx
PsEventIdentifier PsMySender::s_eventId( "eventId" ) ;  

void PsMySender::compute() 
{
  ...
  // pour un événement
  sendValuedEvent( _eventProcess, s_eventId, 5 ) ;
  // pour un signal
  fireValuedSignal( s_eventId, 5 ) ;
}

Notez que _eventProcess est un PsName qui doit être initialisé dans le constructeur de l'émetteur (le plus souvent un nom lu dans les paramètres) donnant le nom (l'identifiant) du récepteur. Dans le récepteur on déclare aussi un PsEventIdentifier pour optimiser la comparaison lors de la réception du message.

// .h
class PsMyReciever : public PsSimulatedObject
{
  ...
protected:
  PsEventIdentifier _eventId;
} ;
//.cxx
PsMyReciever::PsMyReciever( PsController& ctrl, 
		            const PsObjectDescriptor& objectDescriptor)
: PsSimulatedObject( ctrl, objectDescriptor ),
  _eventId( PsMySender::s_eventId )
{
  ...
}
bool PsMyReciever:: processEvent ( PsEvent * e )
{
  if( e->eventId == _eventId )
  { 
    ...
  }
  ...
}

Notez que dans le constructeur pour initialiser le _eventId on peut utiliser la static déclarée publiquement dans l'émetteur ce qui évite les erreurs de copier/coller. Si l'événement ne provient pas forcément d'un objet PsMySender on peut le charger à partir des paramètres. !!! Pour mémoire : il ne faut pas faire de création implicite de PsName ou PsEventIdentifier à partir d'un texte. Aussi il faut stocker les identifiants dans des variables de classe ou d'instance selon les cas de figure. Sont à proscrire les codes suivants où des convertions implicites de texte ont lieu :

fireSignal( "PsNameServer_va_chercher_un_bon_moment_cet_identifiant" ) ;
sendValuedEvent( "Le_nom_de_l_objet_récepteur", "Le_nom_de_l_événement", 5 ) ;
if( e->eventId == "Pour_celui_ci_ce_n_est_pas_mieux" ) { ... }

retour


Q: Puis-je utiliser des static dans un objet simulé ?

Ca dépend, car la distribution qui est à la base d'OpenMASK a des conséquences. Une variable de classe (static) sera distribuée et donc présente en plusieurs exemplaires, chaque processus possède sa propre instance, elle n'est plus unique.

Si on utilise une variable de classe pour gérer une information spécifique au processus, cela fonctionne. Par exemple l'utilisation d'un flag pour savoir si une initialisation a été effectuée au sein du processus. Autre cas qui fonctionne, les constantes, dans la mesure où elles sont initialisées à leur création, elles auront la même valeur sur chacun des processus, néanmoins ce seront bien des instances différentes. En revanche, si c'est pour partager de l'information entre objets d'une même classe, ce n'est pas fonctionnel. Par exemple, compter le nombre d'instances d'une classe en incrémentant une statique dans le constructeur ne fonctionne pas.

retour


Q: Pourquoi dois-je utiliser des héritiers de PsType pour échanger des données entre Objets de Simulation OpenMASK ? Ne serait-ce pas aussi simple avec des types classiques int, float, struct,...?

Pour communiquer entre Objets de Simulation, OpenMASK utilise les méthodes de l'interface PsFlowable permettant d'insérer ou d'extraire des données d'un flux et par là même de distribuer l'information. Avec des types simples cela ne serait pas possible. De plus, l'interface PsType ajoute la notion de polateur qui permet de gérer les données dans le temps.

retour


Q: J'ai un Objet de Simulation "A" qui doit mettre à jour une sortie dans un autre Objet "B" et cela ne fonctionne pas, OpenMASK me dit que l'Objet "B" n'est pas l'objet actif. Comment résoudre ce problème ?

En effet, pour que le set fonctionne il faut que l'objet qui possède l'output soit actif. Cette modification est interdite afin de pouvoir distribuer les Objets de Simulation. Il est a noter qu'il est aussi interdit pour la même raison de distribution de stocker des pointeurs sur les objets pour accéder à ceux-ci, en effet un objet distribué n'a pas une adresse fixe (il peut être mis en miroir sur un autre processus)

La solution est de créer un événement et de l'envoyer à l'objet qui possède l'output. Voici un exemple avec l'objet "A" mettant à jour les outputs de l'objet "B".

Dans A.h

class A : public PsSimulatedObject
{
...
public:
  // l'identifiant du message public et static pour que l'objet B puisse y accéder
  static PsEventIdentifier s_eventId ;
  // le type d'information et d'événement transmis par l'objet A à l'objet B
  typedef PsPair< PsInt, PsFloat > InfoType ;
  typedef PsValuedEvent< InfoType > EventType ;
  // Méthode de classe pour la conversion
  static EventType* getValuedEvent( PsEvent * e ) const 
    { return ( e->eventId == s_eventId ) ? dynamic_cast< EventType * >( e ) : 0 ; }  
  // le nom de l'objet B qui sera lu dans les paramètres de configuration
  PsName _nomRecepteur ;
...
} ;

Dans A.cpp

PsEventIdentifier A::s_eventId( "eventId" ) ;

void A::compute()
{
...
  // L'envoi de l'information
  InfoType info( 1, 1.0f ) ;
  sendValuedEvent( _nomRecepteur, s_eventId, info );
...
}

Dans B.h

class B : public PsSimulatedObject
{
...
protected:
  PsOutput< PsInt   >& _unEntier   ;
  PsOutput< PsFloat >& _unFlottant ;
...
} ;

Dans B.cpp

B::B( PsController& ctrl, const PsObjectDescriptor& desc )
: PsSimulatedObject( ctrl, desc ),
  _unEntier  ( addOutput< PsInt   >( "unEntier"   ) ),
  _unFlottant( addOutput< PsFloat >( "unFlottant" ) )
{
  // initialisation des deux outputs
  _unEntier  .set(    0 ) ;
  _unFlottant.set( 0.0f ) ;
}
bool B::processEvent( PsEvent * e )
{
  bool proceed = false ;
  A::EventType * event = A::getValuedEvent( e );
  if( event )
  { // gestion du message en provenance de A
    _unEntier  .set( event->value.first  ) ;
    _unFlottant.set( event->value.second ) ;
    proceed = true ;
  }
  return proceed || PsSimulatedObject::processEvent( e ) ;
}

retour


Q: Dans un événement valué ou un output je dois transmettre plusieurs paramètres en même temps. Comment faire ?

La règle pour transmettre des paramètres, c'est qu'on doit utiliser un héritier de PsType. Ensuite cela dépend du nombre de paramètres à transmettre.

La suite ici

retour

Q: Existe-t-il des règles de codage pour développer des modules OpenMASK ?

Oui.

Ces règles sont simples et peu nombreuses aussi pour faciliter la réutilisation des modules il est nécessaire de s'y conformer.

Accedez à la page NormeDeCodage

retour

Q: J'utilise rand() pour placer mes objets et pourtant ils apparaissent tous au même endroit. Pourquoi ?

Le problème peut venir du lecteur de VRML d'OpenSG. En effet, celui-ci dans certains cas réinitilise la graine du générateur de nombre aléatoire avec une constante : srand( cte ). Aussi à l'issue de la lecture d'une géométrie donnée on a toujours la même séquence de nombres aléatoires. Pour éviter cela il faut réinitialiser la graine du générateur avant de tirer une nouvelle séquence.

retour

Navigation