Salome HOME
mergefrom branch BR_V511_PR tag mergeto_trunk_03feb09
[modules/yacs.git] / doc / advancepy.rst
1
2 .. _advancepy:
3
4 Utilisation avancée de l'interface de programmation Python
5 ==========================================================================
6
7 Passage d'objets Python entre noeuds de calcul
8 --------------------------------------------------
9 Le modèle de données standard de YACS permet d'échanger un certain nombre de types
10 de données (voir :ref:`datatypes`) qui sont limités aux types supportés par CORBA.
11 Le langage Python permet de manipuler des types de données qui ne sont pas gérés par YACS.
12 En particulier, le dictionnaire Python avec des types de données hétérogènes n'est pas géré
13 par le modèle de données de YACS.
14
15 Il est cependant possible d'échanger des dictionnaires python entre certains types de noeuds de calcul
16 en utilisant des références d'objets avec un protocole non standard. Le protocole standard est
17 le protocole IDL qui correspond à une sérialisation des données gérée par CORBA.
18 Il existe deux autres protocoles (python et json) qui utilisent des mécanismes de sérialisation
19 non CORBA qui supportent plus de types de données dont les dictionnaires.
20 Le nom du protocole apparait dans la première partie du Repositiory ID du type (avant le premier :).
21
22 Le protocole python
23 ++++++++++++++++++++++
24 Le protocole python s'appuie sur une sérialisation faite par le module cPickle (implémentation C
25 du module pickle).
26 Il suffit de définir un type référence d'objet avec le protocole python pour pouvoir échanger
27 des objets python entre noeuds inline python et avec des composants SALOME implémentés en python.
28 Les composants SALOME implémentés en python qui veulent supporter ce type d'échange doivent être
29 conçus pour recevoir une chaine de caractères qui contient l'objet sérialisé. La désérialisation 
30 de l'objet reste à la charge du composant. Dans l'autre sens, la sérialisation est à la charge 
31 du composant qui doit retourner une chaine de caractères pour ce type d'objet.
32
33 Définition du type "objet python"::
34
35   tc1=p.createInterfaceTc("python:obj:1.0","PyObj",[])
36
37 Définition de deux noeuds Python qui utilisent ce type::
38
39   n2=r.createScriptNode("","node2")
40   p.edAddChild(n2)
41   n2.setScript("""
42   p1={'a':1,'b':5.6,'c':[1,2,3]}
43   """)
44   n2.edAddOutputPort("p1",tc1)
45
46   n3=r.createScriptNode("","node3")
47   n3.setScript("""
48   print "p1=",p1
49   """)
50   n3.edAddInputPort("p1",tc1)
51   p.edAddChild(n3)
52   p.edAddDFLink(n2.getOutputPort("p1"),n3.getInputPort("p1"))
53
54 Définition d'un noeud de service SALOME qui utilise ce type::
55
56   n1=r.createCompoNode("","node1")
57   n1.setRef("compo1")
58   n1.setMethod("run")
59   n1.edAddInputPort("p1",tc1)
60   n1.edAddOutputPort("p1",tc1)
61   p.edAddChild(n1)
62   p.edAddDFLink(n2.getOutputPort("p1"),n1.getInputPort("p1"))
63
64 L'implémentation du composant compo1 doit prendre en charge la sérialisation/désérialisation
65 comme dans l'exemple de la méthode run qui suit::
66
67   def run(self,s):
68     o=cPickle.loads(s)
69     ret={'a':6, 'b':[1,2,3]}
70     return cPickle.dumps(ret,-1)
71  
72 Le protocole json
73 ++++++++++++++++++++++
74 Le protocole json s'appuie sur la sérialisation/désérialisation `JSON <http://www.json.org/>`_ 
75 (JavaScript Object Notation) à la place de cPickle. json supporte moins de types de données
76 et nécessite l'installation du module python simplejson mais il a l'avantage d'être
77 plus interopérable. En particulier, il existe des librairies C++ qui sérialisent/désérialisent
78 du JSON.
79
80 Pour utiliser ce protocole dans YACS, il suffit de remplacer python par json dans la définition
81 du type. Par exemple::
82
83   tc1=p.createInterfaceTc("json:obj:1.0","PyObj",[])
84
85 Le reste est identique sauf l'implémentation du composant qui devient en reprenant l'exemple
86 ci-dessus::
87
88   def run(self,s):
89     o=simplejson.loads(s)
90     ret={'a':6, 'b':[1,2,3]}
91     return simplejson.dumps(ret)
92
93 Définition de composants Python inline
94 --------------------------------------------------
95 Normalement, un composant SALOME Python doit être développé en dehors de YACS soit
96 à la main soit en utilisant un générateur de module SALOME comme :ref:`yacsgen`.
97 Il est possible de définir un composant SALOME implémenté en Python directement
98 dans un script python. Ce type de composant peut être utile dans les phases de test,
99 par exemple.
100
101 La première étape consiste à compiler l'interface IDL directement dans le script python
102 ce qui a pour effet de créer les modules Python CORBA nécessaires. Par exemple, voici
103 comment on produit les modules CORBA compo1 et compo1__POA qui contiennent l'interface 
104 base avec une seule méthode run::
105
106   idlcompo="""
107   #include "DSC_Engines.idl"
108   #include "SALOME_Exception.idl"
109   module compo1{
110     interface base :Engines::Superv_Component {
111       string run(in string s) raises (SALOME::SALOME_Exception);
112     };
113   };
114   """
115   m=omniORB.importIDLString(idlcompo,["-I/local/chris/SALOME2/RELEASES/Install/KERNEL_V4_0/idl/salome"])
116
117 La deuxième étape consiste à définir le corps du composant compo1 et donc de sa méthode run.
118
119 Voici un exemple de définition faite dans le corps du script Python::
120
121   import compo1
122   import compo1__POA
123
124   class compo(compo1__POA.base,dsccalcium.PyDSCComponent):
125     def run(self,s):
126       print "+++++++++++run+++++++++++",s
127       return "Bien recu"+s
128
129   compo1.compo1=compo
130
131 Ce qui est important ici, c'est que SALOME trouve dans le module compo1, la classe de même nom
132 qui représente le composant (d'où la dernière ligne).
133
134 La troisième étape consiste à définir un container SALOME local au script car ce composant n'a
135 d'existence que dans le script. La définition d'un container de nom "MyContainerPy" se fera
136 comme suit::
137
138   from omniORB import CORBA
139   from SALOME_ContainerPy import SALOME_ContainerPy_i
140   orb = CORBA.ORB_init(sys.argv, CORBA.ORB_ID)
141   poa = orb.resolve_initial_references("RootPOA")
142   poaManager = poa._get_the_POAManager()
143   poaManager.activate()
144   cpy_i = SALOME_ContainerPy_i(orb, poa, "MyContainerPy")
145
146 en prenant bien garde à activer CORBA avec poaManager.activate().
147
148 Ensuite, il ne reste plus qu'à créer un container YACS et à y placer un noeud SALOME 
149 comme pour un composant standard.