Salome HOME
cd3fba48043fed4621f4223d8a98d5e64689d29d
[modules/yacs.git] / doc / schemapy.rst
1
2 .. _schemapy:
3
4 Définition d'un schéma de calcul avec l'interface de programmation Python
5 ============================================================================
6 Un schéma de calcul YACS peut être défini à partir d'un programme écrit en langage Python (http://www.python.org/).
7 Pour une initiation au langage, on consultera le `tutorial Python <http://docs.python.org/tut/tut.html>`_.
8
9 L'interface de programmation (API) est portée par trois modules Python : pilot, SALOMERuntime et loader.
10
11 Le module SALOMERuntime sert pour initialiser YACS pour SALOME.
12
13 Le module loader sert pour créer des schémas de calcul en chargeant des fichiers au format XML.
14
15 Le module pilot est celui qui sert à créer des schémas de calcul.
16
17 Ces modules doivent être importés au début du programme Python et YACS doit être initialisé::
18
19     import sys
20     import pilot
21     import SALOMERuntime
22     import loader
23     SALOMERuntime.RuntimeSALOME_setRuntime()
24
25 Pour pouvoir importer les modules YACS, l'environnement doit être correctement configuré ce qui est le 
26 cas si on utilise l'application SALOME. Sinon, il faut positionner la variable d'environnement PYTHONPATH
27 à <YACS_ROOT_DIR>/lib/lib/pythonX.Y/site-packages/salome.
28
29 .. _loadxml:
30
31 Créer un schéma de calcul par chargement d'un fichier XML
32 --------------------------------------------------------------
33 C'est la façon la plus simple de créer un schéma de calcul. Si on a un fichier conforme à la syntaxe YACS
34 (voir :ref:`schemaxml`), il suffit de créer un chargeur de fichier XML puis d'utiliser sa méthode load
35 pour obtenir un objet schéma de calcul en Python.
36
37 Voici le code Python suffisant pour charger un fichier XML::
38
39   xmlLoader = loader.YACSLoader()
40   try:
41     p = xmlLoader.load("simple1.xml")
42   except IOError,ex:
43     print "IO exception:",ex
44     sys.exit(1)
45
46 Ensuite, si on met dans un fichier de nom testLoader.py le code de l'initialisation et le code
47 du chargement, il suffit de faire::
48
49   python testLoader.py
50
51 pour exécuter le programme. L'exception IOError peut être levée par l'opération de chargement 
52 principalement si le fichier n'existe pas ou ne peut pas être lu.
53 Dans le cas où aucune exception n'a été levé, il faut vérifier que l'analyse du fichier s'est
54 bien passée. Ceci est fait en utilisant l'objet Logger associé au schéma de calcul::
55
56    logger=p.getLogger("parser")
57    if not logger.isEmpty():
58      print "The imported file has errors :"
59      print logger.getStr()
60      sys.exit(1)
61
62 Enfin, si l'analyse du fichier s'est bien passée, il faut vérifier la validité du schéma (complétude
63 des connexions, pas de port d'entrée non connecté, ...). On utilise pour celà la méthode isValid de 
64 l'objet schéma de calcul puis la méthode p.checkConsistency de ce même objet comme ci-dessous::
65
66    if not p.isValid():
67      print "The schema is not valid and can not be executed"
68      print p.getErrorReport()
69      sys.exit(1)
70
71    info=pilot.LinkInfo(pilot.LinkInfo.ALL_DONT_STOP)
72    p.checkConsistency(info)
73    if info.areWarningsOrErrors():
74      print "The schema is not consistent and can not be executed"
75      print info.getGlobalRepr()
76      sys.exit(1)
77
78
79 Si tous ces tests se sont bien passés, le schéma est prêt à être exécuté (voir :ref:`execpy`).
80
81 Créer un schéma de calcul de zéro
82 -----------------------------------
83 On suivra ici la même progression que dans :ref:`schemaxml`.
84
85 La première chose à faire avant de créer les objets constitutifs du schéma est d'obtenir
86 l'objet runtime qui va servir pour leur création::
87
88   r = pilot.getRuntime()
89
90 Création d'un schéma vide
91 ''''''''''''''''''''''''''''
92 On l'obtient par en utilisant la méthode createProc de l'objet runtime avec le nom
93 du schéma en argument::
94  
95   p=r.createProc("pr")
96
97 L'objet schéma de nom "pr" a été créé. Il est représenté par l'objet Python p.
98
99 Définition des types de données
100 '''''''''''''''''''''''''''''''''
101 Types de base
102 ++++++++++++++++
103 On ne peut définir de type de base. Ils sont définis par YACS. Il faut cependant pouvoir
104 récupérer un objet Python équivalent à un type de base pour pouvoir créer par la suite des
105 ports.
106
107 On récupère un type de données de base en utilisant la méthode getTypeCode du schéma de calcul
108 avec le nom du type en argument. Par exemple::
109
110    td=p.getTypeCode("double")
111
112 permet d'obtenir le type double (objet Python td).
113 Les autres types de base s'obtiennent par::
114
115    ti=p.getTypeCode("int")
116    ts=p.getTypeCode("string")
117    tb=p.getTypeCode("bool")
118    tf=p.getTypeCode("file")
119
120
121 Référence d'objet
122 +++++++++++++++++++++
123 Pour définir un type référence d'objet, on utilise la méthode createInterfaceTc du schéma de calcul. Cette méthode
124 prend trois arguments : le repository id de l'objet SALOME correspondant, le nom du type, une liste de types
125 qui seront des types de base de ce type. Si le repository id vaut "", la valeur par défaut sera utilisée.
126
127 Voici un exemple minimal de définition de référence d'objet de nom Obj (repository id par défaut, pas de type de base)::
128
129   tc1=p.createInterfaceTc("","Obj",[])
130
131 On peut définir le même type Obj, en donnant le repository id::
132
133   tc1=p.createInterfaceTc("IDL:GEOM/GEOM_Object","Obj",[])
134
135 Pour définir un type référence d'objet dérivé d'un autre type, on fournit en plus une liste de types de base.
136
137 Voici la définition du type MyObj dérivé du type Obj::
138
139   tc2=p.createInterfaceTc("","MyObj",[tc1])
140
141 Séquence
142 +++++++++++
143 Pour définir un type séquence, on utilise la méthode createSequenceTc du schéma de calcul. Cette méthode
144 prend trois arguments : le repository id, le nom du type, le type des éléments de la séquence. Il n'est
145 généralement pas utile de spécifier le repository id. On donnera la valeur "".
146
147 Voici un exemple de définition du type séquence de double seqdbl::
148
149   tc3=p.createSequenceTc("","seqdbl",td)
150
151 td est le type double que l'on obtiendra comme ci-dessus : `Types de base`_.
152
153 Pour définir un type séquence de séquence, on écrit::
154
155   tc4=p.createSequenceTc("","seqseqdbl",tc3)
156
157 Pour définir un type séquence de référence, on écrit::
158
159   tc5=p.createSequenceTc("","seqobj",tc1)
160
161
162 Structure
163 ++++++++++++
164 Pour définir un type structure, on utilise la méthode createStructTc du schéma de calcul. Cette méthode
165 prend deux arguments : le repository id, le nom du type. Pour une utilisation standard, le repository
166 id prendra la valeur "". Le type structure est le seul qui se définit en deux étapes. Il est créé
167 vide suite à l'appel de la méthode createStructTc. Pour définir ses membres, il faut ensuite
168
169 Voici un exemple de définition du type structure s1 avec 2 membres (m1 et m2) de types double et séquence de doubles::
170
171   ts1=p.createStructTc("","s1")
172   ts1.addMember("m1",td);
173   ts1.addMember("m2",tc3);
174
175 Récupérer les types prédéfinis
176 +++++++++++++++++++++++++++++++++
177 Par défaut, YACS définit seulement les types de base. Pour obtenir plus de types prédéfinis, il faut
178 les demander à SALOME. Ces autres types prédéfinis sont contenus dans les catalogues des modules
179 comme GEOM ou SMESH.
180
181 La séquence de code qui permet d'obtenir une image des catalogues SALOME dans YACS est la suivante::
182
183   try:
184     cata=r.loadCatalog("session","corbaname::localhost:2810/NameService#Kernel.dir/ModulCatalog.object")
185   except CORBA.TRANSIENT,ex:
186     print "Unable to contact server:",ex
187   except CORBA.SystemException,ex:
188     print ex,CORBA.id(ex)
189
190 Il faut que l'application SALOME ait été lancée pour que le catalogue soit accessible.
191 Ensuite, les types prédéfinis sont accessibles dans le dictionnaire cata._typeMap.
192 Si on connait le nom du type voulu ('GEOM_Shape', par exemple), on l'obtient par::
193
194   tgeom=cata._typeMap['GEOM_Shape']
195
196 .. _typedict:
197
198 Ajouter un type dans le dictionnaire des types du schéma
199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
200 Certaines opérations nécessitent d'avoir les types définis dans le dictionnaire du schéma.
201 Pour mettre un type dans le dictionnaire, on fait, par exemple pour le type seqobj défini ci-dessus::
202
203   p.typeMap["seqobj"]=tc5
204
205 avec le nom du type comme clé du dictionnaire et le type comme valeur.
206
207 Définition des noeuds de calcul élémentaires
208 ''''''''''''''''''''''''''''''''''''''''''''''
209 Noeud script Python
210 +++++++++++++++++++++
211 Pour définir un noeud script dans un contexte donné (le schéma de calcul, par exemple), on
212 procède en plusieurs étapes. 
213 La première étape consiste à créer l'objet noeud par appel à la méthode createScriptNode du runtime.
214 Cette méthode a 2 arguments dont le premier doit valoir "" en utilisation standard et le deuxième
215 est le nom du noeud. Voici un exemple de création du noeud node1::
216
217   n=r.createScriptNode("","node1")
218
219 La deuxième étape consiste à rattacher le noeud à son contexte de définition par appel à la méthode
220 edAddChild de l'objet contexte. Cette méthode a un argument : le noeud à rattacher. Voici un exemple
221 de rattachement du noeud node1 au schéma de calcul::
222
223   p.edAddChild(n)
224
225 Attention, le nom de la méthode à utiliser dépend du type de noeud contexte. On verra plus tard pour d'autres
226 types de noeud quelle méthode utiliser.
227
228 La troisième étape consiste à définir le script Python associé au noeud. On utilise pour celà la méthode setScript
229 du noeud avec un argument chaine de caractères qui contient le code Python. Voici un exemple de définition
230 du code associé::
231
232   n.setScript("p1=p1+2.5")
233
234 La quatrième étape consiste à définir les ports de données d'entrée et de sortie. Un port d'entrée est créé par appel
235 à la méthode edAddInputPort du noeud. Un port de sortie est créé par appel à la méthode edAddOutputPort du noeud.
236 Ces deux méthodes ont deux arguments : le nom du port et le type de données du port. Voici un exemple de création
237 d'un port d'entrée p1 de type double et d'un port de sortie p1 de type double::
238
239   n.edAddInputPort("p1",td)
240   n.edAddOutputPort("p1",td)
241
242 Maintenant notre noeud est complètement défini avec son nom, son script, ses ports et son contexte. Il récupère
243 un double dans le port d'entrée p1, lui ajoute 2.5 et met le résultat dans le port de sortie p1.
244
245 Noeud fonction Python
246 ++++++++++++++++++++++
247 Pour définir un noeud fonction, on procède de la même manière. Les seules différences concernent la création :
248 utiliser la méthode createFuncNode et la définition de la fonction : il faut en plus appeler la méthode setFname
249 pour donner le nom de la fonction à exécuter. Voici un exemple complet de définition d'un noeud fonction
250 qui est fonctionnellement identique au noeud script précédent::
251
252   n2=r.createFuncNode("","node2")
253   p.edAddChild(n2)
254   n2.setScript("""
255   def f(p1):
256     p1=p1+2.5
257     return p1
258   """)
259   n2.setFname("f")
260   n2.edAddInputPort("p1",td)
261   n2.edAddOutputPort("p1",td)
262
263
264 Noeud de service SALOME
265 ++++++++++++++++++++++++++
266 On a deux formes de définition d'un noeud de service SALOME.
267
268 La première forme dans laquelle on donne le nom du composant utilise la méthode createCompoNode
269 pour la création du noeud. Le nom du composant est donné en argument de la méthode setRef du noeud.
270 Le nom du service est donné en argument de la méthode setMethod du noeud. Le reste de la définition est 
271 identique à celui des noeuds Python précédents.
272
273 Voici un exemple de noeud qui appelle le service makeBanner d'un composant PYHELLO::
274
275   n3=r.createCompoNode("","node3")
276   p.edAddChild(n3)
277   n3.setRef("PYHELLO")
278   n3.setMethod("makeBanner")
279   n3.edAddInputPort("p1",ts)
280   n3.edAddOutputPort("p1",ts)
281
282 La deuxième forme qui permet de définir un noeud qui utilise le même composant qu'un autre noeud
283 utilise la méthode createNode de ce dernier noeud. Cette méthode n'a qu'un argument, le nom du noeud.
284 Le reste de la définition est identique à celui de la précédente forme.
285
286 Voici un exemple de noeud de service qui appelle une deuxième fois le service makeBanner de la même
287 instance de composant que le noeud précédent::
288
289   n4=n3.createNode("node4")
290   p.edAddChild(n4)
291   n4.setMethod("makeBanner")
292   n4.edAddInputPort("p1",ts)
293   n4.edAddOutputPort("p1",ts)
294
295 Définition des connexions
296 ''''''''''''''''''''''''''''
297 Obtenir un port d'un noeud
298 ++++++++++++++++++++++++++++
299 Pour pouvoir définir des liens, il faut presque toujours disposer des objets Python représentant
300 le port de sortie à connecter au port d'entrée.
301 Il y a deux façons de disposer de cet objet.
302
303 La première façon est de récupérer le port lors de sa création avec les méthodes edAddInputPort et 
304 edAddOutputPort. On écrira alors, par exemple::
305
306   pin=n4.edAddInputPort("p1",ts)
307   pout=n4.edAddOutputPort("p1",ts)
308
309 pin et pout sont alors les objets nécessaires pour définir des liens.
310
311 La deuxième façon est d'interroger le noeud et de lui demander un de ses ports par son nom. On utilise pour
312 celà les méthodes getInputPort et getOutputPort.
313
314 On pourra alors obtenir pin et pout comme suit::
315
316   pin=n4.getInputPort("p1")
317   pout=n4.getOutputPort("p1")
318
319 Lien de contrôle
320 ++++++++++++++++++
321 Pour définir un lien de contrôle entre deux noeuds, on utilise la méthode edAddCFLink du contexte en lui passant
322 en arguments les deux noeuds à connecter.
323 Par exemple, un lien de contrôle entre les noeuds n3 et n4 s'écrira::
324
325   p.edAddCFLink(n3,n4)
326
327 Le noeud n3 sera exécuté avant le noeud n4.
328
329 Lien dataflow
330 ++++++++++++++
331 Pour définir un lien dataflow, il faut tout d'abord obtenir les objets ports par une des méthodes vues 
332 ci-dessus. Ensuite on utilise la méthode edAddDFLink du noeud contexte en lui passant les deux ports à connecter.
333
334 Voici un exemple de lien dataflow entre le port de sortie p1 du noeud n3 et le port d'entrée du noeud n4::
335
336   pout=n3.getOutputPort("p1")
337   pin=n4.getInputPort("p1")
338   p.edAddDFLink(pout,pin)
339
340 Lien data
341 ++++++++++++
342 Un lien data se définit comme un lien dataflow en utilisant la méthode edAddLink au lieu de edAddDFLink.
343 Le même exemple que ci-dessus avec un lien data::
344
345   pout=n3.getOutputPort("p1")
346   pin=n4.getInputPort("p1")
347   p.edAddLink(pout,pin)
348
349 Initialisation d'un port de données d'entrée
350 '''''''''''''''''''''''''''''''''''''''''''''''
351 Pour initialiser un port de données d'entrée, il faut tout d'abord obtenir l'objet port correspondant. Ensuite, il existe
352 deux méthodes pour l'initialiser.
353
354 La première initialise le port avec une valeur encodée en XML-RPC. On utilise alors la méthode edInitXML du
355 port. Voici un exemple qui initialise un port avec la valeur entière 5::
356
357   pin.edInitXML("<value><int>5</int></value>")
358
359 La deuxième méthode initialise le port avec une valeur Python. On utilise alors la méthode edInitPy. Voici
360 un exemple qui initialise ce même port avec la même valeur::
361
362   pin.edInitPy(5)
363
364 On peut également utiliser des méthodes spécifiques pour les types de base :
365   
366 - ``edInitInt`` pour le type int
367 - ``edInitDbl`` pour le type double
368 - ``edInitBool`` pour le type bool
369 - ``edInitString`` pour le type string
370
371 Premier exemple à partir des éléments précédents
372 '''''''''''''''''''''''''''''''''''''''''''''''''''
373 En rassemblant tous les éléments de définition précédents, un schéma de calcul complet identique à celui
374 du chapitre :ref:`schemaxml` se présentera comme suit::
375
376   import sys
377   import pilot
378   import SALOMERuntime
379   import loader
380   SALOMERuntime.RuntimeSALOME_setRuntime()
381   r = pilot.getRuntime()
382   p=r.createProc("pr")
383   ti=p.getTypeCode("int")
384   #node1
385   n1=r.createScriptNode("","node1")
386   p.edAddChild(n1)
387   n1.setScript("p1=p1+10")
388   n1.edAddInputPort("p1",ti)
389   n1.edAddOutputPort("p1",ti)
390   #node2
391   n2=r.createScriptNode("","node2")
392   p.edAddChild(n2)
393   n2.setScript("p1=2*p1")
394   n2.edAddInputPort("p1",ti)
395   n2.edAddOutputPort("p1",ti)
396   #node4
397   n4=r.createCompoNode("","node4")
398   p.edAddChild(n4)
399   n4.setRef("ECHO")
400   n4.setMethod("echoDouble")
401   n4.edAddInputPort("p1",td)
402   n4.edAddOutputPort("p1",td)
403   #control links
404   p.edAddCFLink(n1,n2)
405   p.edAddCFLink(n1,n4)
406   #dataflow links
407   pout=n3.getOutputPort("p1")
408   pin=n4.getInputPort("p1")
409   #dataflow links
410   p.edAddDFLink(n1.getOutputPort("p1"),n2.getInputPort("p1"))
411   p.edAddDFLink(n1.getOutputPort("p1"),n4.getInputPort("p1"))
412   #initialisation ports
413   n1.getInputPort("p1").edInitPy(5)
414
415
416 Définition de noeuds composés
417 '''''''''''''''''''''''''''''''''
418 Bloc
419 +++++++
420 Pour définir un Bloc, on utilise la méthode createBloc du runtime en lui passant le nom du Bloc en argument. Ensuite,
421 on rattache le noeud à son contexte de définition comme un noeud élémentaire. Voici un exemple de définition de Bloc
422 dans un schéma de calcul::
423
424   b=r.createBloc("b1")
425   p.edAddChild(b)
426
427 Une fois le Bloc créé, il est possible d'ajouter tous les noeuds et liens possibles dans son contexte. En reprenant une partie
428 de l'exemple ci-dessus, on aura::
429
430   n1=r.createScriptNode("","node1")
431   b.edAddChild(n1)
432   n1.setScript("p1=p1+10")
433   n1.edAddInputPort("p1",ti)
434   n1.edAddOutputPort("p1",ti)
435   n2=r.createScriptNode("","node2")
436   b.edAddChild(n2)
437   n2.setScript("p1=2*p1")
438   n2.edAddInputPort("p1",ti)
439   n2.edAddOutputPort("p1",ti)
440   b.edAddCFLink(n1,n2)
441   b.edAddDFLink(n1.getOutputPort("p1"),n2.getInputPort("p1"))
442
443 ForLoop
444 ++++++++
445 Pour définir une ForLoop, on utilise la méthode createForLoop du runtime en lui passant le nom du noeud en argument.
446 Ensuite on rattache le noeud à son contexte de définition. Voici un exemple de définition de ForLoop
447 dans un schéma de calcul::
448
449   l=r.createForLoop("l1")
450   p.edAddChild(l)
451
452 Pour initialiser le nombre de tours de boucle à exécuter, on utilise le port "nsteps" que l'on initialise 
453 avec un entier. Par exemple::
454
455   ip=l.getInputPort("nsteps") 
456   ip.edInitPy(3)
457
458 Il existe une méthode spéciale pour obtenir le port "nsteps" de la boucle : edGetNbOfTimesInputPort. On pourrait donc
459 l'écrire également ainsi::
460
461   ip=l.edGetNbOfTimesInputPort()
462   ip.edInitPy(3)
463
464 Enfin, pour ajouter un noeud (et un seul) dans le contexte d'une boucle, on n'utilisera pas la méthode edAddChild
465 mais une méthode différente de nom edSetNode. Voici un petit exemple de définition d'un noeud interne de boucle::
466
467   n1=r.createScriptNode("","node1")
468   l.edSetNode(n1)
469   n1.setScript("p1=p1+10")
470   n1.edAddInputPort("p1",ti)
471   n1.edAddOutputPort("p1",ti)
472
473 WhileLoop
474 ++++++++++
475 Un noeud WhileLoop se définit presque comme un noeud ForLoop. Les seules différences concernent la création et 
476 l'affectation de la condition de fin de boucle. Pour la création on utilise la méthode createWhileLoop. Pour la 
477 condition, on utilise le port "condition". Si on fait un rebouclage sur un noeud, il ne faut pas oublier d'utiliser
478 un lien data et non un lien dataflow.
479 Voici un exemple de définition de noeud WhileLoop avec un noeud interne script Python. La condition est initialisée
480 à True puis passée à False par le noeud interne. On a donc un rebouclage::
481
482   wh=r.createWhileLoop("w1")
483   p.edAddChild(wh)
484   n=r.createScriptNode("","node3")
485   n.setScript("p1=0")
486   n.edAddOutputPort("p1",ti)
487   wh.edSetNode(n)
488   cport=wh.getInputPort("condition")
489   cport.edInitBool(True)
490   p.edAddLink(n.getOutputPort("p1"),cport)
491
492 Il existe une méthode spéciale pour obtenir le port "condition" de la boucle : edGetConditionPort.
493
494 Boucle ForEach
495 ++++++++++++++++
496 Un noeud ForEach se définit à la base comme un autre noeud boucle. Il y a plusieurs différences. Le noeud
497 est créé avec la méthode createForEachLoop qui prend un argument de plus : le type de données géré par le ForEach.
498 Le nombre de branches du ForEach est spécifié avec le port "nbBranches". La gestion de la collection sur 
499 laquelle itère le ForEach est faite par connexion des ports "SmplPrt" et "SmplsCollection".
500
501 Voici un exemple de définition de noeud ForEach avec un noeud interne script Python qui incrémente l'élément
502 de la collection de 3.::
503
504   fe=r.createForEachLoop("fe1",td)
505   p.edAddChild(fe)
506   n=r.createScriptNode("","node3")
507   n.setScript("p1=p1+3.")
508   n.edAddInputPort("p1",td)
509   n.edAddOutputPort("p1",td)
510   fe.edSetNode(n)
511   p.edAddLink(fe.getOutputPort("SmplPrt"),n.getInputPort("p1"))
512   fe.getInputPort("nbBranches").edInitPy(3)
513   fe.getInputPort("SmplsCollection").edInitPy([2.,3.,4.])
514
515 Pour obtenir les ports spéciaux du ForEach, on peut utiliser les méthodes suivantes à la place de getInputPort et 
516 getOutputPort :
517
518 - edGetNbOfBranchesPort pour le port "nbBranches"
519 - edGetSamplePort pour le port "SmplPrt"
520 - edGetSeqOfSamplesPort pour le port "SmplsCollection"
521
522 Switch
523 ++++++++
524 La définition d'un noeud Switch se fait en plusieurs étapes. Les deux premières sont la création et le rattachment
525 au noeud de contexte. Le noeud est créé par appel de la méthode createSwitch du runtime avec le nom du noeud
526 en argument. Le noeud est rattaché au noeud contexte par appel de la méthode edAddChild pour un schéma ou un bloc
527 ou edSetNode pour un noeud boucle.
528
529 Voici un exemple de création suivi du rattachement::
530
531   sw=r.createSwitch("sw1")
532   p.edAddChild(sw)
533
534 Ensuite on crée un noeud interne élémentaire ou composé par cas. Le noeud pour le cas par défaut est
535 rattaché au switch avec la méthode edSetDefaultNode. Les noeuds pour les autres cas sont rattachés au switch
536 avec la méthode edSetNode qui prend en premier argument la valeur du cas (entier) et en deuxième argument
537 le noeud interne.
538
539 Voici un exemple de switch avec un noeud script pour le cas "1" et un autre noeud script
540 pour le cas "default" et un noeud script pour initialiser une variable échangée::
541
542   #init
543   n=r.createScriptNode("","node3")
544   n.setScript("p1=3.5")
545   n.edAddOutputPort("p1",td)
546   p.edAddChild(n)
547   #switch
548   sw=r.createSwitch("sw1")
549   p.edAddChild(sw)
550   nk1=r.createScriptNode("","ncas1")
551   nk1.setScript("p1=p1+3.")
552   nk1.edAddInputPort("p1",td)
553   nk1.edAddOutputPort("p1",td)
554   sw.edSetNode(1,nk1)
555   ndef=r.createScriptNode("","ndefault")
556   ndef.setScript("p1=p1+5.")
557   ndef.edAddInputPort("p1",td)
558   ndef.edAddOutputPort("p1",td)
559   sw.edSetDefaultNode(ndef)
560   #initialisation port select
561   sw.getInputPort("select").edInitPy(1)
562   #connexion des noeuds internes
563   p.edAddDFLink(n.getOutputPort("p1"),nk1.getInputPort("p1"))
564   p.edAddDFLink(n.getOutputPort("p1"),ndef.getInputPort("p1"))
565
566 Pour obtenir le port spécial "select" du Switch, on peut utiliser la méthode edGetConditionPort à la place de getInputPort.
567   
568 Définition de containers
569 ''''''''''''''''''''''''''''
570 Pour définir un container, on utilise la méthode createContainer du runtime puis on lui donne un nom avec sa méthode 
571 setName. Enfin on lui affecte des contraintes en lui ajoutant des propriétés.
572 Voici un exemple de création d'un container de nom "A"::
573
574   c1=r.createContainer()
575   c1.setName("A")
576
577 On ajoute une propriété à un container en utilisant sa méthode setProperty qui prend 2 arguments (chaines de caractères).
578 Le premier est le nom de la propriété. Le deuxième est sa valeur.
579 Voici un exemple du même container "A" avec des contraintes::
580
581   c1=r.createContainer()
582   c1.setName("A")
583   c1.setProperty("container_name","FactoryServer")
584   c1.setProperty("hostname","localhost")
585   c1.setProperty("mem_mb","1000")
586
587 Une fois que les containers sont définis, on peut placer des composants SALOME sur ce container. 
588 Pour placer le composant d'un noeud de service SALOME, il faut tout d'abord obtenir l'instance de
589 composant de ce noeud de service en utilisant la méthode getComponent de ce noeud. Puis on affecte le container
590 précédemment défini à cette instance de composant en utilisant la méthode setContainer de l'instance
591 de composant.
592
593 Si on veut placer le service SALOME défini plus haut (noeud "node3") sur le container "A", on écrira::
594
595   n3.getComponent().setContainer(c1)
596
597
598 Les propriétés de noeuds
599 '''''''''''''''''''''''''''
600 On ajoute (ou modifie) une propriété à un noeud élémentaire ou composé en utilisant sa méthode
601 setProperty qui prend 2 arguments (chaines de caractères).
602 Le premier est le nom de la propriété. Le deuxième est sa valeur.
603
604 Voici un exemple pour le noeud "node3" précédent::
605
606   n3.setProperty("VERBOSE","2")
607
608 Les connexions datastream
609 ''''''''''''''''''''''''''''
610 Les connexions datastream ne sont possibles que pour des noeuds de service SALOME comme on l'a vu dans Principes. 
611 Il faut tout d'abord définir les ports datastream dans le noeud de service. Un port datastream d'entrée se définit
612 en utilisant la méthode edAddInputDataStreamPort. Un port datastream de sortie se définit
613 en utilisant la méthode edAddOutputDataStreamPort.
614 Ces méthodes prennent en arguments le nom du port et le type du datastream.
615
616 Certains ports datastream (par exemple les ports CALCIUM) doivent être configurés avec des propriétés.
617 On utilise la méthode setProperty du port pour les configurer.
618
619 Voici un exemple de définition de noeud de service SALOME avec des ports datastream. 
620 Il s'agit du composant DSCCODC que l'on peut trouver dans le module DSCCODES de la base EXAMPLES. 
621 Les ports datastream sont de type "CALCIUM_integer" avec dépendance temporelle::
622
623   calcium_int=cata._typeMap['CALCIUM_integer']
624   n5=r.createCompoNode("","node5")
625   p.edAddChild(n5)
626   n5.setRef("DSCCODC")
627   n5.setMethod("prun")
628   pin=n5.edAddInputDataStreamPort("ETP_EN",calcium_int)
629   pin.setProperty("DependencyType","TIME_DEPENDENCY")
630   pout=n5.edAddOutputDataStreamPort("STP_EN",calcium_int)
631   pout.setProperty("DependencyType","TIME_DEPENDENCY")
632
633 Une fois les noeuds de service dotés de ports datastream, il ne reste plus qu'à les connecter.
634 Cette connexion est réalisée en utilisant la méthode edAddLink du noeud contexte comme pour 
635 les liens data. Seul le type des ports passés en arguments est différent.
636
637 Pour compléter notre exemple on définit un deuxième noeud de service et on connecte les 
638 ports datastream de ces services::
639
640   n6=r.createCompoNode("","node6")
641   p.edAddChild(n6)
642   n6.setRef("DSCCODD")
643   n6.setMethod("prun")
644   pin=n6.edAddInputDataStreamPort("ETP_EN",calcium_int)
645   pin.setProperty("DependencyType","TIME_DEPENDENCY")
646   pout=n6.edAddOutputDataStreamPort("STP_EN",calcium_int)
647   pout.setProperty("DependencyType","TIME_DEPENDENCY")
648   p.edAddLink(n5.getOutputDataStreamPort("STP_EN"),n6.getInputDataStreamPort("ETP_EN"))
649   p.edAddLink(n6.getOutputDataStreamPort("STP_EN"),n5.getInputDataStreamPort("ETP_EN"))
650
651 D'autres noeuds élémentaires
652 '''''''''''''''''''''''''''''''
653 Noeud SalomePython
654 +++++++++++++++++++
655 La définition d'un noeud SalomePython est quasiment identique à celle d'un `Noeud fonction Python`_. On utilise
656 la méthode createSInlineNode du runtime à la place de createFuncNode et on ajoute une information de
657 placement sur un container comme pour un noeud de service SALOME (méthode setContainer).
658
659 Voici un exemple semblable à celui de :ref:`schemaxml`::
660
661   n2=r.createSInlineNode("","node2")
662   p.edAddChild(n2)
663   n2.setScript("""
664   import salome
665   salome.salome_init()
666   import PYHELLO_ORB
667   def f(p1):
668     print __container__from__YACS__
669     machine,container=__container__from__YACS__.split('/')
670     param={'hostname':machine,'container_name':container}
671     compo=salome.lcc.LoadComponent(param, "PYHELLO")
672     print compo.makeBanner(p1)
673     print p1
674   """)
675   n2.setFname("f")
676   n2.edAddInputPort("p1",ts)
677   n2.getComponent().setContainer(c1)
678
679 Noeud DataIn
680 +++++++++++++++
681 Pour définir un noeud DataIn, on utilise la méthode createInDataNode du runtime. Elle prend deux arguments
682 dont le premier doit être "" et le deuxième le nom du noeud.
683 Pour définir les données du noeud, on lui ajoute des ports de données de sortie avec la méthode edAddOutputPort
684 en lui passant le nom de la donnée et son type en arguments.
685 Pour initialiser la valeur de la donnée, on utilise la méthode setData du port ainsi créé en lui passant 
686 la valeur encodée en XML-RPC (voir :ref:`initialisation`).
687
688 Voici un exemple de noeud DataIn qui définit 2 données de type double (b et c) et une donnée de type fichier (f)::
689
690   n=r.createInDataNode("","data1")
691   p.edAddChild(n)
692   pout=n.edAddOutputPort('a',td)
693   pout.setData("<value><double>-1.</double></value>")
694   pout=n.edAddOutputPort('b',td)
695   pout.setData("<value><double>5.</double></value>")
696   pout=n.edAddOutputPort('f',tf)
697   pout.setData("<value><objref>f.data</objref></value>")
698   
699 Il est possible d'affecter une valeur à une donnée directement avec un objet Python en utilisant
700 la méthode setDataPy. Exemple pour une séquence::
701
702   pout.setDataPy([1.,5.])
703
704 Noeud DataOut
705 +++++++++++++++++
706 Pour définir un noeud DataOut, on utilise la méthode createOutDataNode du runtime. Elle prend deux arguments
707 dont le premier doit être "" et le deuxième le nom du noeud.
708 Pour définir les résultats du noeud, on lui ajoute des ports de données d'entrée en utilisant la 
709 méthode edAddInputPort avec le nom du résultat et son type en arguments.
710 Pour sauvegarder les résultats dans un fichier on utilise la méthode setRef du noeud avec le nom
711 du fichier en argument.
712 Pour recopier un résultat fichier dans un fichier local, on utilise la méthode setData du port correspondant
713 au résultat avec le nom du fichier en argument.
714
715 Voici un exemple de noeud DataOut qui définit des résultats (a, b, c, d, f) de différents 
716 types (double, int, string, vecteur de doubles, fichier) et écrit les valeurs correspondantes 
717 dans le fichier g.data. Le fichier résultat sera copié dans le fichier local monfich::
718
719   n=r.createOutDataNode("","data2")
720   n.setRef("g.data")
721   p.edAddChild(n)
722   n.edAddInputPort('a',td)
723   n.edAddInputPort('b',ti)
724   n.edAddInputPort('c',ts)
725   n.edAddInputPort('d',tc3)
726   pin=n.edAddInputPort('f',tf)
727   pin.setData("monfich")
728
729 Noeud StudyIn
730 ++++++++++++++
731 Pour définir un noeud StudyIn, on utilise la méthode createInDataNode du runtime. Elle prend deux arguments
732 dont le premier doit être "study" et le deuxième le nom du noeud.
733 Pour spécifier l'étude associée, on ajoute la propriété "StudyID" au noeud en utilisant sa méthode setProperty.
734 Pour définir les données du noeud, on lui ajoute des ports de données de sortie avec la méthode edAddOutputPort
735 en lui passant le nom de la donnée et son type en arguments.
736 Pour initialiser la donnée avec la référence dans l'étude, on utilise la méthode setData du port ainsi créé 
737 en lui passant une chaine de caractères qui contient soit l'Entry SALOME soit le chemin dans l'arbre d'étude.
738
739 Voici un exemple de noeud StudyIn qui définit 2 données de type GEOM_Object (a et b). 
740 L'étude est supposée chargée en mémoire par SALOME sous le StudyID 1. 
741 La donnée a est référencée par une Entry SALOME. La donnée b est référencée par un chemin dans l'arbre d'étude::
742
743   n=r.createInDataNode("study","study1")
744   p.edAddChild(n)
745   n.setProperty("StudyID","1")
746   pout=n.edAddOutputPort('a',tgeom)
747   pout.setData("0:1:1:1")
748   pout=n.edAddOutputPort('b',tgeom)
749   pout.setData("/Geometry/Sphere_1")
750
751
752 Noeud StudyOut
753 ++++++++++++++++
754 Pour définir un noeud StudyOut, on utilise la méthode createOutDataNode du runtime. Elle prend deux arguments
755 dont le premier doit être "study" et le deuxième le nom du noeud.
756 Pour spécifier l'étude associée, on ajoute la propriété "StudyID" au noeud en utilisant sa méthode setProperty.
757 Pour spécifier un nom de fichier dans lequel sera sauvegardée l'étude, on utilise la méthode setRef du noeud
758 avec le nom du fichier en argument.
759 Pour définir les résultats du noeud, on lui ajoute des ports de données d'entrée avec la méthode edAddInputPort
760 en lui passant le nom de la donnée et son type en arguments.
761 Pour associer l'entrée dans l'étude au résultat, on utilise la méthode setData du port 
762 en lui passant une chaine de caractères qui contient soit l'Entry SALOME soit le chemin dans l'arbre d'étude.
763
764 Voici un exemple de noeud StudyOut qui définit 2 résultats (a et b) de type GEOM_Object. 
765 L'étude utilisée a le studyId 1. Le résultat a est référencé par une Entry SALOME.
766 Le résultat b est référencé par un chemin.
767 L'étude complète est sauvegardée en fin de calcul dans le fichier study1.hdf::
768
769   n=r.createOutDataNode("study","study2")
770   n.setRef("study1.hdf")
771   p.edAddChild(n)
772   n.setProperty("StudyID","1")
773   pout=n.edAddInputPort('a',tgeom)
774   pout.setData("0:1:2:1")
775   pout=n.edAddInputPort('b',tgeom)
776   pout.setData("/Save/Sphere_2")
777
778
779 Sauvegarder un schéma de calcul dans un fichier XML
780 ------------------------------------------------------
781 Pour sauvegarder un schéma de calcul dans un fichier au format XML, il faut utiliser la méthode saveSchema
782 du schéma de calcul en lui passant le nom du fichier en argument.
783 Pour qu'un schéma de calcul, construit en Python, puisse être sauvegardé sous une forme cohérente
784 dans un fichier XML, il ne faut pas oublier d'ajouter dans le dictionnaire des types du schéma
785 tous les types qui ont été définis en Python (voir :ref:`typedict`). La sauvegarde ne le fait pas automatiquement. 
786
787 Pour sauver le schéma p construit ci-dessus, dans le fichier monschema.xml, il faut écrire::
788
789   p.saveSchema("monschema.xml")
790
791 Le fichier ainsi obtenu pourra ensuite être chargé comme dans :ref:`loadxml`.
792
793
794 Quelques opérations utiles
795 ------------------------------
796 Retrouver un noeud par son nom
797 '''''''''''''''''''''''''''''''''''
798 Pour retrouver un noeud (objet Python) quand on n'a que l'objet schéma de calcul et le nom absolu du noeud, il 
799 suffit d'appeler la méthode getChildByName du schéma en lui passant le nom absolu.
800
801 Pour retrouver le noeud script Python défini en `Noeud script Python`_::
802
803   n=p.getChildByName("node1")
804
805 Pour retrouver le noeud "node1" dans le bloc "b1"::
806
807   n=p.getChildByName("b1.node1")
808
809 Cette opération est également utilisable à partir d'un noeud composé à condition d'utiliser le nom
810 relatif du noeud.
811 On peut réécrire l'exemple précédent::
812
813   n=b.getChildByName("node1")
814
815 Retrouver un port par son nom
816 ''''''''''''''''''''''''''''''''
817 Pour retrouver un port d'un noeud par son nom, il faut d'abord récupérer le noeud par son nom. Puis
818 on retrouve un port de données d'entrée avec la méthode getInputPort et un port de données de sortie
819 avec la méthode getOutputPort.
820
821 Voici un exemple à partir du noeud n précédent::
822
823   pin=n.getOutputPort("p1")
824   pout=n.getInputPort("p2")
825
826 Obtenir la valeur d'un port
827 ''''''''''''''''''''''''''''''''
828 Pour obtenir la valeur d'un port on utilise sa méthode getPyObj.
829 Exemple::
830
831   print pin.getPyObj()
832   print pout.getPyObj()
833
834 Obtenir l'état d'un noeud
835 ''''''''''''''''''''''''''''
836 Pour obtenir l'état d'un noeud on utilise sa méthode getEffectiveState (voir les valeurs
837 posssibles dans :ref:`etats`)
838
839 Retirer un noeud de son contexte
840 ''''''''''''''''''''''''''''''''''
841 Il est possible de retirer un noeud de son noeud contexte en utilisant une méthode du contexte. 
842 Le nom de la méthode diffère selon le type de contexte.
843
844 - Pour un Bloc ou un schéma de calcul on utilisera la méthode edRemoveChild avec le noeud à retirer en argument::
845
846     p.edRemoveChild(n)
847
848 - Pour une boucle (ForLoop, WhileLoop ou ForEachLoop) on utilisera la méthode edRemoveNode sans argument::
849
850     l.edRemoveNode()
851
852 - Pour un Switch, on utilisera la méthode edRemoveChild avec le noeud interne concerné en argument::
853
854     sw.edRemoveChild(nk1)
855