Salome HOME
copy tag mergefrom_BR_V0_1_CC_Salome_04oct07
[modules/yacs.git] / src / salomeloader / salomeloader.py
1 # -*- coding: iso-8859-1 -*-
2 """Ce module sert pour lire un schema de calcul Salome décrit en XML.
3    et le convertir en schema de calcul YACS
4
5    Cette lecture est réalisée au moyen de la classe SalomeLoader 
6    et de sa méthode load.
7
8 """
9
10 import sys,os
11 import cElementTree as ElementTree
12 from sets import Set
13 import pilot
14 import SALOMERuntime
15
16 class UnknownKind(Exception):pass
17
18 debug=0
19 typeMap={}
20 streamTypes={
21              '0':"Unknown",
22              '1':"CALCIUM_int",
23              '3':"CALCIUM_real",
24             }
25
26 #Les fonctions suivantes : invert, reachable,InducedSubgraph,write_dot,display
27 #permettent de realiser des operations sur un graphe.
28 #Est considere comme un graphe un objet G qui supporte les
29 #operations suivantes : l'iteration sur les noeuds (for n in G parcourt
30 #tous les noeuds du graphe) et l'iteration sur les suivants
31 # (for v in G[n] parcourt tous les suivants du noeud n)
32 def invert(G):
33   """Construit le graphe inverse de G en inversant les liens de voisinage"""
34   I={}
35   for n in G:
36     I.setdefault(n,Set())
37     for v in G[n]:
38       I.setdefault(v,Set()).add(n)
39   return I
40
41 def reachable(G,n):
42   """Construit le set de noeuds atteignables depuis le noeud n
43
44      Le noeud n n'est pas dans le set retourné sauf en cas de boucles
45      Ce cas n'est pas traité ici (limitation)
46   """
47   s=G[n]
48   for v in G[n]:
49     s=s|reachable(G,v)
50   return s
51
52 def InducedSubgraph(V,G,adjacency_list_type=Set):
53   """ Construit un sous graphe de G avec les noeuds contenus dans V  """
54   def neighbors(x):
55     for y in G[x]:
56       if y in V:
57         yield y
58   return dict([(x,adjacency_list_type(neighbors(x))) for x in G if x in V])
59
60 def write_dot(stream,G):
61   """Ecrit la representation (au format dot) du graphe G dans le fichier stream"""
62   name="toto"
63   stream.write('digraph %s {\nnode [ style="filled" ]\n' % name)
64   for node in G :
65     try:
66       label = "%s:%s"% (node.name,node.__class__.__name__)
67     except:
68       label=str(node)
69     color='green'
70     stream.write('   %s [fillcolor="%s" label=< %s >];\n' % ( id(node), color, label))
71   for src in G:
72     for dst in G[src]:
73       stream.write('   %s -> %s;\n' % (id(src), id(dst)))
74   stream.write("}\n")
75
76 def display(G,suivi="sync"):
77   """Affiche le graphe G avec l'outil dot"""
78   f=file("graph.dot", 'w')
79   write_dot(f,G)
80   f.close()
81   cmd="dot -Tpng graph.dot |display" + (suivi == "async" and "&" or "")
82   os.system(cmd)
83       
84
85 def test():
86   G={
87   1:Set([2,3]),
88   2:Set([4]),
89   3:Set([5]),
90   4:Set([6]),
91   5:Set([6]),
92   6:Set(),
93   }
94   display(G)
95   I=invert(G)
96   print reachable(G,2)
97   print reachable(I,6)
98   print reachable(G,2) & reachable(I,6)
99
100 #Fin des fonctions graphe
101
102 currentProc=None
103
104 class SalomeLoader:
105   """Loader de schéma Salome natif et convertisseur en schéma 'nouvelle formule'.
106      La méthode loadxml parse le fichier xml et retourne un objet SalomeProc 
107      natif non converti
108      La méthode load parse le fichier xml et retourne un objet représentant 
109      un schéma Salome converti (objet Proc de YACS)
110   """
111
112   def loadxml(self,filename):
113     """
114        Lit un fichier XML du SUPERV de Salome et retourne la liste des 
115        procedures (instances de SalomeProc)
116     """
117     tree = ElementTree.ElementTree(file=filename)
118     root = tree.getroot()
119     if debug:print "root.tag:",root.tag,root
120
121     procs=[]
122     if root.tag == "dataflow":
123       #un seul dataflow
124       dataflow=root
125       if debug:print dataflow
126       proc=SalomeProc(dataflow)
127       procs.append(proc)
128     else:
129       #un ou plusieurs dataflow. Le schema contient des macros. 
130       # Toutes les macros sont
131       #décrites au meme niveau dans le fichier XML.
132       for dataflow in root.findall("dataflow"):
133         if debug:print dataflow
134         proc=SalomeProc(dataflow)
135         if debug:print "nom du dataflow:",proc.name
136         procs.append(proc)
137     return procs
138
139   def load(self,filename):
140     """Lit un fichier XML et convertit les procedures lues en procedures 
141        nouvelle formule
142     """
143     global currentProc
144     procs=self.loadxml(filename)
145     #on récupère le schema de tete d'un coté et les éventuelles 
146     #macros de l'autre: procs[0]
147     proc=procs.pop(0)
148     #proc.display()
149
150     #Enregistrement des éventuelles macros dans macro_dict
151     macro_dict={}
152     for p in procs:
153       if debug:print "proc_name:",p.name,"coupled_node:",p.coupled_node
154       macro_dict[p.name]=p
155
156     if debug:print filename
157     yacsproc=ProcNode(proc,macro_dict)
158     return yacsproc.createNode()
159
160 class Service:
161     """Classe pour porter les caractéristiques d'un service"""
162 class Parameter:
163     """Classe pour porter les caractéristiques d'un parametre"""
164 class Link:
165     """Classe pour porter les caractéristiques d'un link"""
166 class Data:
167     """Classe pour porter les caractéristiques d'un data"""
168
169 class Node:
170     """Node de calcul simple : classe de base à spécialiser """
171     label="Node: "
172     def __init__(self):
173       self.links=[]    # liste pour stocker les entrees sous forme de link
174       # le link doit avoir deux attributs : from_node qui donne le node origine 
175       # et to_node qui donne le node cible
176       self.datas=[]
177       self.inStreamLinks=[] #liste des liens dataStream associés à ce node (in)
178       self.outStreamLinks=[] #liste des liens dataStream associés à ce node (out)
179     def createNode(self):
180       raise NotImplementedError
181     def getInputPort(self,p):
182       return self.node.getInputPort(".".join(p.split("__")))
183     def getOutputPort(self,p):
184       return self.node.getOutputPort(".".join(p.split("__")))
185     def getInputDataStreamPort(self,p):
186       return self.node.getInputDataStreamPort(p)
187     def getOutputDataStreamPort(self,p):
188       return self.node.getOutputDataStreamPort(p)
189
190 class InlineNode(Node):
191     """Node de calcul inline salome : fonction dans self.codes[0]"""
192     def __init__(self):
193       Node.__init__(self)
194       self.codes=[]
195     def createNode(self):
196       r = pilot.getRuntime()
197       if self.fnames[0] == "?":
198         n=r.createScriptNode("",self.name)
199       else:
200         n=r.createFuncNode("",self.name)
201         n.setFname(self.fnames[0])
202         n.setScript(self.codes[0])
203       self.node=n
204       for para in self.service.inParameters:
205         if not typeMap.has_key(para.type):
206           #create the missing type and add it in type map
207           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
208           currentProc.typeMap[para.type]=typeMap[para.type]
209         n.edAddInputPort(para.name,typeMap[para.type])
210       for para in self.service.outParameters:
211         if not typeMap.has_key(para.type):
212           #create the missing type and add it in type map
213           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
214           currentProc.typeMap[para.type]=typeMap[para.type]
215         n.edAddOutputPort(para.name,typeMap[para.type])
216
217       return n
218
219 class ComputeNode(Node):
220     """Node de calcul pour exécuter un service Salome"""
221     def createNode(self):
222       r = pilot.getRuntime()
223       n=r.createCompoNode("",self.name)
224       n.setRef(self.sComponent)
225       n.setMethod(self.service.name)
226       self.node=n
227
228       #ajout des ports in et out du service
229       for para in self.service.inParameters:
230         if not typeMap.has_key(para.type):
231           #on cree le type manquant et on l'ajoute dans la table des types
232           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
233           currentProc.typeMap[para.type]=typeMap[para.type]
234         n.edAddInputPort(para.name,typeMap[para.type])
235       for para in self.service.outParameters:
236         if not typeMap.has_key(para.type):
237           #on cree le type manquant et on l'ajoute dans la table des types
238           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
239           currentProc.typeMap[para.type]=typeMap[para.type]
240         pout=n.edAddOutputPort(para.name,typeMap[para.type])
241
242       #ajout des ports datastream in et out
243       for para in self.inStreams:
244         if debug:print para.name,para.type,para.dependency,para.schema, para.interpolation,
245         if debug:print para.extrapolation
246         pin=n.edAddInputDataStreamPort(para.name,typeMap[streamTypes[para.type]])
247       for para in self.outStreams:
248         if debug:print para.name,para.type,para.dependency,para.values
249         pout=n.edAddOutputDataStreamPort(para.name,typeMap[streamTypes[para.type]])
250
251       return n
252
253 class ComposedNode(Node):
254   """Node de calcul composite Salome (classe de base)"""
255
256   def reduceLoop(self):
257     """Transforme un graphe de type Salome avec les boucles
258        a plat en un graphe hierarchique 
259        Le graphe de depart (Salome) est dans self.G.
260        On le transforme en place
261     """
262     G=self.G
263     if debug:display(G)
264     #calcul du graphe inverse
265     I=invert(G)
266     #display(I)
267
268     #on recherche toutes les boucles et leurs noeuds internes
269     loops={}
270     for n in G:
271       if n.kind == 4:
272         #Debut de boucle
273         loops[n]=reachable(G,n)&reachable(I,n.endloop)
274         n.inner_nodes=loops[n]
275         n.G=InducedSubgraph(loops[n],G)
276
277     if debug:print "toutes les boucles du graphe"
278     if debug:print loops
279
280     #on recherche les boucles les plus externes
281     outer_loops=loops.keys()
282     for l in loops:
283       for ll in outer_loops:
284         if loops[l] < loops[ll]:
285           #boucle interne
286           outer_loops.remove(l)
287           ll.set_inner(l)
288           break
289
290     #a la fin, les boucles restantes dans outer_loops sont les plus externes
291     if debug:print outer_loops
292
293     #on supprime les noeuds internes des boucles les plus externes
294     for l in outer_loops:
295       #on enleve les noeuds internes
296       for n in loops[l]:
297         del G[n]
298       #on enleve le noeud endloop
299       suiv=G[l.endloop]
300       del G[l.endloop]
301       #on remplace les noeuds suivants de loop par ceux de endloop
302       G[l]= suiv
303
304       #on tente de transformer les liens entrants et sortants sur le noeud endloop
305       #en noeuds directs. On ne traite probablement pas tous les cas.
306       inputs={}
307       for link in l.endloop.links:
308         if debug:print link.from_node,link.to_node,link.from_param,link.to_param
309         inputs[link.to_param]=link.from_node,link.from_param
310
311       for s in suiv:
312         for link in s.links:
313           if link.from_node == l.endloop:
314             link.from_node,link.from_param=inputs[link.from_param]
315           if debug:print link.from_node,link.to_node,link.from_param,link.to_param
316
317       if debug:display(G)
318
319       #on applique le traitement de reduction aux boucles les plus externes (recursif)
320       for l in outer_loops:
321         l.reduceLoop()
322
323   def connect_macros(self,macro_dict):
324     """Cette methode rattache les macros salome contenues dans macro_dict
325        a la procedure YACS proc
326        On est ici dans le noeud auquel on veut rattacher une des macros
327        macro_dict est un dictionnaire dont la cle est le nom de la macro
328        et la valeur est un graphe Salome (objet SalomeProc)
329        Les noeuds concernes sont les MacroNode et les SalomeProc
330     """
331     if debug:print "connect_macros",self.node,macro_dict
332     for node in self.G:
333       if isinstance(node,MacroNode):
334         #c'est une macro, il faut rattacher sa description
335         #p est le sous graphe Salome (objet SalomeProc)
336         #node est le MacroNode Salome qui utilise le sous graphe p
337         #node.node est le bloc YACS equivalent
338         p=macro_dict[node.coupled_node]
339         bloc=node.node
340         if debug:print "macronode:",node.name,node.coupled_node,p
341         #a partir de la procédure salome a plat on cree un
342         #graphe d'exécution hiérarchique nouvelle formule
343         G=p.create_graph()
344         node.G=G
345         for n in G:
346           #chaque noeud du graphe G cree un noeud YACS equivalent
347           nod=n.createNode()
348           bloc.edAddChild(nod)
349
350         #on demande le rattachement des macros aux nodes du macroNode node
351         node.connect_macros(macro_dict)
352
353         #on ajoute les liens de controle
354         for n in G:
355           for v in G[n]:
356             bloc.edAddCFLink(n.node,v.node)
357         #on ajoute les liens de donnees et les initialisations
358         for n in G:
359           #liens dataflow
360           for l in n.links:
361             bloc.edAddLink(l.from_node.getOutputPort(l.from_param),
362                         l.to_node.getInputPort(l.to_param))
363           #liens datastream
364           for l in n.outStreamLinks:
365             pout=l.from_node.getOutputDataStreamPort(l.from_param)
366             pin=l.to_node.getInputDataStreamPort(l.to_param)
367             bloc.edAddLink(pout,pin)
368           #initialisations
369           for l in n.datas:
370             if l.type == 7:
371               #double
372               n.getInputPort(l.tonodeparam).edInitDbl(l.value)
373             elif l.type == 3:
374               #int
375               n.getInputPort(l.tonodeparam).edInitInt(l.value)
376
377 class LoopNode(ComposedNode):
378     """Objet qui simule le comportement d'une boucle Salome."""
379     def __init__(self):
380       ComposedNode.__init__(self)
381       self.inner_loops=[]
382       #inner_nodes contient les noeuds internes au sens Salome (a plat
383       #avec les noeuds endloop)
384       self.inner_nodes=[]
385
386     def set_node(self,node):
387       self.node=node
388
389     def set_inner(self,loop):
390       for i in self.inner_loops:
391         if loop.inner_nodes < i.inner_nodes:
392           #la boucle est contenue dans i
393           i.set_inner(loop)
394           break
395       self.inner_loops.append(loop)
396
397     def createNode(self):
398       """Cree l'objet boucle equivalent
399
400          Un objet boucle Salome a n ports d'entrée et les memes ports en sortie.
401          La tete de boucle a 3 fonctions : init, next, more qui ont des signatures
402          tres voisines. init et next ont la meme signature : en entree les parametres
403          d'entree de la boucle et en sortie les parametres de sortie de la boucle c'est
404          à dire les memes qu'en entrée. more a les memes parametres d'entree et a un 
405          parametre de sortie supplementaire qui vient en premiere position. Ce 
406          parametre indique si la boucle doit etre poursuivie ou stoppée.
407          La fin de boucle a une fonction qui a la meme signature que next.
408
409          Pour transformer ce type de boucle, on crée un ensemble de noeuds de calcul
410          regroupés dans un bloc. Dans ce bloc, on crée un noeud externe pour init suivi
411          d'une boucle while.
412          Ensuite on crée un bloc qui contiendra 2 noeuds (next et more) plus tous 
413          les noeuds internes de la boucle.
414       """
415
416       r = pilot.getRuntime()
417       bloop=r.createBloc(self.name)
418
419       #noeud init
420       init=r.createFuncNode("","init")
421       #print self.codes[0]
422       init.setScript(self.codes[0])
423       init.setFname(self.fnames[0])
424       for para in self.service.inParameters:
425         if not typeMap.has_key(para.type):
426           #create the missing type and add it in type map
427           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
428           currentProc.typeMap[para.type]=typeMap[para.type]
429         init.edAddInputPort(para.name,typeMap[para.type])
430       for para in self.service.outParameters:
431         if not typeMap.has_key(para.type):
432           #create the missing type and add it in type map
433           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
434           currentProc.typeMap[para.type]=typeMap[para.type]
435         init.edAddOutputPort(para.name,typeMap[para.type])
436       bloop.edAddChild(init)
437       self.init=init
438
439       wh=r.createWhileLoop(self.name)
440       bloop.edAddChild(wh)
441       blnode=r.createBloc(self.name)
442       wh.edSetNode(blnode)
443       cport=wh.edGetConditionPort()
444       cport.edInitBool(True)
445
446       #noeud next
447       next=r.createFuncNode("","next")
448       #print self.codes[2]
449       next.setScript(self.codes[2])
450       next.setFname(self.fnames[2])
451       for para in self.service.inParameters:
452         if not typeMap.has_key(para.type):
453           #create the missing type and add it in type map
454           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
455           currentProc.typeMap[para.type]=typeMap[para.type]
456         next.edAddInputPort(para.name,typeMap[para.type])
457       for para in self.service.outParameters:
458         if not typeMap.has_key(para.type):
459           #create the missing type and add it in type map
460           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
461           currentProc.typeMap[para.type]=typeMap[para.type]
462         next.edAddOutputPort(para.name,typeMap[para.type])
463       blnode.edAddChild(next)
464       self.next=next
465
466       #noeud more
467       more=r.createFuncNode("","more")
468       #print self.codes[1]
469       more.setScript(self.codes[1])
470       more.setFname(self.fnames[1])
471       for para in self.service.inParameters:
472         if not typeMap.has_key(para.type):
473           #create the missing type and add it in type map
474           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
475           currentProc.typeMap[para.type]=typeMap[para.type]
476         more.edAddInputPort(para.name,typeMap[para.type])
477       more.edAddOutputPort("DoLoop",typeMap["int"])
478       for para in self.service.outParameters:
479         if not typeMap.has_key(para.type):
480           #create the missing type and add it in type map
481           typeMap[para.type]= currentProc.createInterfaceTc("",para.type,[])
482           currentProc.typeMap[para.type]=typeMap[para.type]
483         more.edAddOutputPort(para.name,typeMap[para.type])
484       blnode.edAddChild(more)
485       self.more=more
486
487       for para in self.service.outParameters:
488         bloop.edAddDFLink(init.getOutputPort(para.name),next.getInputPort(para.name))
489
490       for para in self.service.outParameters:
491         blnode.edAddDFLink(next.getOutputPort(para.name),more.getInputPort(para.name))
492
493       wh.edAddLink(more.getOutputPort("DoLoop"),wh.getInputPort("condition"))
494
495       for para in self.service.outParameters:
496         wh.edAddLink(more.getOutputPort(para.name),next.getInputPort(para.name))
497
498       self.node=bloop
499
500       for n in self.G:
501         node=n.createNode()
502         blnode.edAddChild(node)
503
504       for n in self.G:
505         for v in self.G[n]:
506           blnode.edAddCFLink(n.node,v.node)
507
508       for n in self.G:
509         for l in n.links:
510           print l.from_node.name,l.to_node.name
511           print l.from_param,l.to_param
512           blnode.edAddDFLink(l.from_node.getOutputPort(l.from_param),
513                              l.to_node.getInputPort(l.to_param))
514
515       return bloop
516
517     def getInputPort(self,p):
518       return self.init.getInputPort(p)
519
520     def getOutputPort(self,p):
521       return self.more.getOutputPort(p)
522
523 class Bloc(ComposedNode):
524     """ Objet composé d'un ensemble de nodes enchaines et qui se 
525         comporte comme un node simple.
526     """
527     label="Bloc: "
528     def __init__(self):
529       Node.__init__(self)
530       self.nodes=[]
531
532     def addLink(self,node1,node2):
533       if node1 not in self.nodes:self.nodes.append(node1)
534       if node2 not in self.nodes:self.nodes.append(node2)
535
536 class MacroNode(Bloc):
537   """Objet qui représente une Macro Salome c'est a dire un node 
538      composite avec une interface : ports in et out.
539   """
540   def createNode(self):
541     """Cree l'objet correspondant a une Macro Salome : un Bloc"""
542     r = pilot.getRuntime()
543     macro=r.createBloc(self.name)
544     self.node=macro
545     return macro
546
547 def is_loop(n):
548   """Indique si n est un node de début de boucle"""
549   return isinstance(n,LoopNode)
550
551 class ProcNode(ComposedNode):
552   """Procedure YACS equivalente a une procedure Salome accompagnee
553      de ses macros
554   """
555   def __init__(self,proc,macro_dict):
556     ComposedNode.__init__(self)
557     self.proc=proc
558     self.macro_dict=macro_dict
559
560   def createNode(self):
561     """Cree l'objet YACS equivalent"""
562     global currentProc
563     r = pilot.getRuntime()
564
565     #create_graph retourne un graphe representatif de la
566     #procedure Salome transformee en un graphe hierarchique
567     G=self.proc.create_graph()
568     self.G=G
569
570     #on utilise le graphe G pour construire la
571     #procedure YACS equivalente p
572     p=r.createProc("pr")
573     self.node=p
574     currentProc=p
575     typeMap["double"]=p.typeMap["double"]
576     typeMap["int"]=p.typeMap["int"]
577     typeMap["long"]=p.typeMap["int"]
578     typeMap["string"]=p.typeMap["string"]
579     typeMap["bool"]=p.typeMap["bool"]
580     typeMap["Unknown"]=p.createInterfaceTc("","Unknown",[])
581     typeMap["GEOM_Object"]=p.createInterfaceTc("","GEOM_Object",[])
582     typeMap["GEOM_Shape"]=typeMap["GEOM_Object"]
583     typeMap["CALCIUM_int"]=p.createInterfaceTc("","CALCIUM_int",[])
584     typeMap["CALCIUM_real"]=p.createInterfaceTc("","CALCIUM_real",[])
585     currentProc.typeMap["Unknown"]=typeMap["Unknown"]
586     currentProc.typeMap["GEOM_Object"]=typeMap["GEOM_Object"]
587     currentProc.typeMap["GEOM_Shape"]=typeMap["GEOM_Shape"]
588     currentProc.typeMap["CALCIUM_int"]=typeMap["CALCIUM_int"]
589     currentProc.typeMap["CALCIUM_real"]=typeMap["CALCIUM_real"]
590
591     for n in G:
592       #chaque noeud du graphe G cree un noeud YACS equivalent
593       node=n.createNode()
594       p.edAddChild(node)
595
596     #on demande le rattachement des macros aux nodes de la procédure p
597     self.connect_macros(self.macro_dict)
598
599     #on ajoute les liens de controle
600     for n in G:
601       for v in G[n]:
602         p.edAddCFLink(n.node,v.node)
603
604     #on ajoute les liens de donnees et les initialisations
605     for n in G:
606       #liens dataflow
607       for l in n.links:
608         print l.from_node.name,l.to_node.name
609         print l.from_param,l.to_param
610         p.edAddLink(l.from_node.getOutputPort(l.from_param),
611                     l.to_node.getInputPort(l.to_param))
612
613       #liens datastream
614       for l in n.outStreamLinks:
615         pout=l.from_node.getOutputDataStreamPort(l.from_param)
616         pin=l.to_node.getInputDataStreamPort(l.to_param)
617         p.edAddLink(pout,pin)
618       #initialisations
619       for l in n.datas:
620         if l.type == 7:
621           #double
622           n.getInputPort(l.tonodeparam).edInitDbl(l.value)
623         elif l.type == 3:
624           #int
625           n.getInputPort(l.tonodeparam).edInitInt(l.value)
626
627     return p
628
629
630 class SalomeProc(ComposedNode):
631     """Objet pour décrire un schéma Salome natif avec ses liens 
632        dataflow, datastream et gate
633        L'objet est construit en parsant un fichier XML
634     """
635     def __init__(self,dataflow):
636         self.name="name"
637         self.parse(dataflow)
638         #self.links : liste des liens dataflow du graphe (objets Link)
639         #self.nodes : liste des noeuds du graphe
640         #self.node_dict : le dictionnaire des noeuds (nom,node)
641         #self.datas : liste des datas du graphe
642         #chaque noeud a 2 listes de liens datastream (inStreams, outStreams)
643
644     def parse(self,dataflow):
645         if debug:print "Tous les noeuds XML"
646         for node in dataflow:
647             if debug:print node.tag,node
648
649         #Récupération des informations du dataflow
650         self.dataflow_info=self.parseService(dataflow.find("info-list/node/service"))
651         if debug:print self.dataflow_info
652         if debug:print self.dataflow_info.inParameters
653         if debug:print self.dataflow_info.outParameters
654         if debug:
655             for para in self.dataflow_info.inParameters:
656                 print "inParam:",para.name,para.name.split("__",1)
657
658         self.name=dataflow.findtext("info-list/node/node-name")
659         self.coupled_node=dataflow.findtext("info-list/node/coupled-node")
660
661         if debug:print "Tous les noeuds XML dataflow/node-list"
662         nodes=[]
663         node_dict={}
664         #on parcourt tous les noeuds
665         for n in dataflow.findall('node-list/node'):
666             #n est un node de node-list
667             kind=n.findtext("kind")
668             comp=n.find("component-name").text
669             name=n.find("node-name").text
670             coupled_node=n.find("coupled-node").text
671             interface=n.find("interface-name").text
672             container=n.find("container").text
673
674             #kind=1 : dataflow ?
675             #kind=2 : ?
676             #kind=9 : schema avec datastream ?
677
678             if kind == "0":
679               #Il s'agit d'un service
680               node=ComputeNode()
681               node.kind=0
682               node.sComponent = comp
683               node.interface=interface
684             elif kind == "3":
685               #il s'agit d'une fonction
686               node=InlineNode()
687               node.kind=3
688               codes=[]
689               fnames=[]
690               for pyfunc in n.findall("PyFunction-list/PyFunction"):
691                 fnames.append(pyfunc.findtext("FuncName"))
692                 codes.append(self.parsePyFunction(pyfunc))
693               node.fnames=fnames
694               node.codes=codes
695             elif kind == "4":
696               #si kind vaut 4 on a une boucle : on crée un LoopNode
697               #les fonctions python (next, more, init) sont stockées dans codes
698               node=LoopNode()
699               node.kind=4
700               codes=[]
701               fnames=[]
702               for pyfunc in n.findall("PyFunction-list/PyFunction"):
703                 fnames.append(pyfunc.findtext("FuncName"))
704                 codes.append(self.parsePyFunction(pyfunc))
705               node.fnames=fnames
706               node.codes=codes
707             elif kind == "5":
708               #noeud de fin de boucle : on crée un InlineNode
709               node=InlineNode()
710               node.kind=5
711               codes=[]
712               fnames=[]
713               for pyfunc in n.findall("PyFunction-list/PyFunction"):
714                 fnames.append(pyfunc.findtext("FuncName"))
715                 codes.append(self.parsePyFunction(pyfunc))
716               node.fnames=fnames
717               node.codes=codes
718             elif kind == "10":
719               #si kind vaut 10 on a un noeud Macro : on cree un MacroNode
720               node=MacroNode()
721               node.kind=10
722             else:
723               raise UnknownKind,kind
724
725             node.name=name
726             node.container=container
727             node.service=None
728             node.coupled_node=coupled_node
729             #on stocke les noeuds dans un dictionnaire pour faciliter les recherches
730             node_dict[node.name]=node
731             if debug:print "\tnode-name",node.name
732             if debug:print "\tkind",node.kind,node.__class__.__name__
733             if debug:print "\tcontainer",node.container
734
735             s=n.find("service")
736             if s:
737                 node.service=self.parseService(s)
738
739
740             #on parcourt les ports datastream
741             if debug:print "DataStream"
742             inStreams=[]
743             for indata in n.findall("DataStream-list/inParameter"):
744                 inStreams.append(self.parseInData(indata))
745             node.inStreams=inStreams
746             outStreams=[]
747             outStreams_dict={}
748             for outdata in n.findall("DataStream-list/outParameter"):
749                 p=self.parseOutData(outdata)
750                 outStreams.append(p)
751                 outStreams_dict[p.name]=p
752             node.outStreams=outStreams
753             node.outStreams_dict=outStreams_dict
754             if debug:print "\t++++++++++++++++++++++++++++++++++++++++++++"
755             nodes.append(node)
756
757         self.nodes=nodes
758         self.node_dict=node_dict
759         #Le parcours des noeuds est fini.
760         #On parcourt les connexions dataflow et datastream
761         """
762         <link>
763         <fromnode-name>Node_A_1</fromnode-name>
764         <fromserviceparameter-name>a_1</fromserviceparameter-name>
765         <tonode-name>Node_B_1</tonode-name>
766         <toserviceparameter-name>b_1</toserviceparameter-name>
767         <coord-list/>
768         </link>
769         """
770         if debug:print "Tous les noeuds XML dataflow/link-list"
771         links=[]
772         if debug:print "\t++++++++++++++++++++++++++++++++++++++++++++"
773         for link in dataflow.findall('link-list/link'):
774             l=Link()
775             l.from_name=link.findtext("fromnode-name")
776             l.to_name=link.findtext("tonode-name")
777             l.from_param=link.findtext("fromserviceparameter-name")
778             l.to_param=link.findtext("toserviceparameter-name")
779             links.append(l)
780             if debug:print "\tfromnode-name",l.from_name
781             if debug:print "\tfromserviceparameter-name",l.from_param
782             if debug:print "\ttonode-name",l.to_name
783             if debug:print "\ttoserviceparameter-name",l.to_param
784             if debug:print "\t++++++++++++++++++++++++++++++++++++++++++++"
785
786         self.links=links
787         if debug:print "Tous les noeuds XML dataflow/data-list"
788         datas=[]
789         for data in dataflow.findall('data-list/data'):
790             d=self.parseData(data)
791             datas.append(d)
792             if debug:print "\ttonode-name",d.tonode
793             if debug:print "\ttoserviceparameter-name",d.tonodeparam
794             if debug:print "\tparameter-value",d.value
795             if debug:print "\tparameter-type",d.type
796             if debug:print "\t++++++++++++++++++++++++++++++++++++++++++++"
797
798         self.datas=datas
799
800     def parseService(self,s):
801         service=Service()
802         service.name=s.findtext("service-name")
803         if debug:print "\tservice-name",service.name
804
805         inParameters=[]
806         for inParam in s.findall("inParameter-list/inParameter"):
807             p=Parameter()
808             p.name=inParam.findtext("inParameter-name")
809             p.type=inParam.findtext("inParameter-type")
810             if debug:print "\tinParameter-name",p.name
811             if debug:print "\tinParameter-type",p.type
812             inParameters.append(p)
813         service.inParameters=inParameters
814         if debug:print "\t++++++++++++++++++++++++++++++++++++++++++++"
815
816         outParameters=[]
817         for outParam in s.findall("outParameter-list/outParameter"):
818             p=Parameter()
819             p.name=outParam.findtext("outParameter-name")
820             p.type=outParam.findtext("outParameter-type")
821             if debug:print "\toutParameter-name",p.name
822             if debug:print "\toutParameter-type",p.type
823             outParameters.append(p)
824         service.outParameters=outParameters
825         if debug:print "\t++++++++++++++++++++++++++++++++++++++++++++"
826         return service
827
828     def parseData(self,d):
829         da=Data()
830         da.tonode=d.findtext("tonode-name")
831         da.tonodeparam=d.findtext("toserviceparameter-name")
832         da.value=d.findtext("data-value/value")
833         da.type=eval(d.findtext("data-value/value-type"))
834         if da.type < 9:
835             da.value=eval(da.value)
836         return da
837
838     def parsePyFunction(self,pyfunc):
839         if debug:print pyfunc.tag,":",pyfunc
840         if debug:print "\tFuncName",pyfunc.findtext("FuncName")
841         text=""
842         for cdata in pyfunc.findall("PyFunc"):
843             if text:text=text+'\n'
844             text=text+ cdata.text
845         return text
846
847     """<inParameter-type>1</inParameter-type>
848     <inParameter-name>istream</inParameter-name>
849     <inParameter-dependency>2</inParameter-dependency>
850     <inParameter-schema>0</inParameter-schema>
851     <inParameter-interpolation>0</inParameter-interpolation>
852     <inParameter-extrapolation>0</inParameter-extrapolation>
853     </inParameter>
854     <outParameter>
855     <outParameter-type>1</outParameter-type>
856     <outParameter-name>ostream</outParameter-name>
857     <outParameter-dependency>2</outParameter-dependency>
858     <outParameter-values>0</outParameter-values>
859     </outParameter>
860     """
861
862     def parseInData(self,d):
863         if debug:print d.tag,":",d
864         p=Parameter()
865         p.name=d.findtext("inParameter-name")
866         p.type=d.findtext("inParameter-type")
867         p.dependency=d.findtext("inParameter-dependency")
868         p.schema=d.findtext("inParameter-schema")
869         p.interpolation=d.findtext("inParameter-interpolation")
870         p.extrapolation=d.findtext("inParameter-extrapolation")
871         if debug:print "\tinParameter-name",p.name
872         return p
873
874     def parseOutData(self,d):
875         if debug:print d.tag,":",d
876         p=Parameter()
877         p.name=d.findtext("outParameter-name")
878         p.type=d.findtext("outParameter-type")
879         p.dependency=d.findtext("outParameter-dependency")
880         p.values=d.findtext("outParameter-values")
881         if debug:print "\toutParameter-name",p.name
882         return p
883
884     def create_graph(self):
885       #un graphe est un dictionnaire dont la clé est un noeud et la valeur
886       #est la liste (en fait un set sans doublon) des noeuds voisins suivants
887       #for v in graphe (python >= 2.3): parcourt les noeuds du graphe
888       #for v in graphe[noeud] parcourt les voisins (suivants) de noeud
889       G={}
890       #on cree tous les noeuds avec un voisinage (suivants) vide
891       for n in self.nodes:
892         G[n]=Set()
893
894       #on construit le voisinage en fonction des divers liens
895       for link in self.links:
896         from_node=self.node_dict[link.from_name]
897         if link.from_param == "Gate" or link.to_param == "Gate":
898           #control link salome : on ajoute le noeud to_name dans les voisins
899           if debug:print "ajout control link",link.from_name,link.to_name
900           G[self.node_dict[link.from_name]].add(self.node_dict[link.to_name])
901
902         elif from_node.outStreams_dict.has_key(link.from_param):
903           #lien datastream salome : 
904           # 1- on ajoute le lien dans les listes de liens des noeuds
905           # 2- on ajoute dans le lien des pointeurs sur les noeuds (from_node et to_node)
906           if debug:print "ajout stream link",link.from_name,link.to_name
907           self.node_dict[link.to_name].inStreamLinks.append(link)
908           self.node_dict[link.from_name].outStreamLinks.append(link)
909           link.from_node=self.node_dict[link.from_name]
910           link.to_node=self.node_dict[link.to_name]
911
912         else:
913           #autre lien salome 
914           #si c'est un lien entre Loop node et EndOfLoop node, on l'ignore
915           #tous les autres sont conservés
916           from_node=self.node_dict[link.from_name]
917           to_node=self.node_dict[link.to_name]
918           if isinstance(to_node,LoopNode):
919             #Est-ce le lien entre EndOfLoop et Loop ? Si oui, on ne le garde pas
920             if to_node.coupled_node == from_node.name:
921               if debug:print "lien arriere loop:",from_node,to_node
922               #lien ignoré
923               continue
924           if debug:print "ajout dataflow link",link.from_name,link.to_name
925           G[self.node_dict[link.from_name]].add(self.node_dict[link.to_name])
926
927           if link.from_param != "DoLoop" and link.to_param != "DoLoop":
928             #Les liens sur parametre DoLoop servent au superviseur Salome, on les ignore
929             #on ajoute dans le lien des pointeurs sur les noeuds (from_node et to_node)
930             #on ajoute ce lien à la liste des liens du noeud cible (to) 
931             self.node_dict[link.to_name].links.append(link)
932             link.from_node=self.node_dict[link.from_name]
933             link.to_node=self.node_dict[link.to_name]
934
935           #Dans un graphe salome avec boucles, les noeuds de tete et de fin
936           #de boucle sont reliés par 2 liens de sens opposés
937           #on stocke le noeud de fin dans l'attribut endloop du noeud de tete.
938           if link.from_param == "DoLoop" and link.to_param == "DoLoop" \
939              and is_loop(self.node_dict[link.from_name]) \
940              and isinstance(self.node_dict[link.to_name],InlineNode):
941             #on repère le node inline de fin de boucle en le stockant 
942             #dans l'attribut endloop du node loop
943             #self.node_dict[link.to_name] est le node de fin de boucle 
944             #de self.node_dict[link.from_name]
945             if debug:print "ajout loop",link.from_name,link.to_name
946             self.node_dict[link.from_name].endloop=self.node_dict[link.to_name]
947             self.node_dict[link.to_name].loop=self.node_dict[link.from_name]
948
949       for data in self.datas:
950         self.node_dict[data.tonode].datas.append(data)
951
952       self.G=G
953
954       #on modifie le graphe en place : 
955       # transformation des boucles a plat en graphe hierarchique
956       self.reduceLoop()
957
958       #on peut maintenant créer le schéma de calcul YACS
959       return G
960
961     def display(self,suivi="sync"):
962         """Visualise la procedure Salome avec dot"""
963         #pour visualiser : dot -Tpng salome.dot |display
964         f=file("salome.dot", 'w')
965         self.write_dot(f)
966         f.close()
967         cmd="dot -Tpng salome.dot |display" + (suivi == "async" and "&" or "")
968         os.system(cmd)
969
970     def write_dot(self,stream):
971         """Dumpe la procedure Salome dans stream au format dot"""
972         stream.write('digraph %s {\nnode [ style="filled" ]\n' % self.name)
973         for node in self.nodes:
974             label = "%s:%s"% (node.name,node.__class__.__name__)
975             color='green'
976             stream.write('   %s [fillcolor="%s" label=< %s >];\n' % (
977                     id(node), color, label
978                 ))
979         for link in self.links:
980             from_node=self.node_dict[link.from_name]
981             to_node=self.node_dict[link.to_name]
982             stream.write('   %s -> %s;\n' % (id(from_node), id(to_node)))
983         stream.write("}\n")
984
985 if __name__ == "__main__":
986
987   import traceback
988   usage ="""Usage: %s salomeFile convertedFile
989     where salomeFile is the name of the input schema file (old Salome syntax)
990     and convertedFile is the name of the output schema file (new YACS syntax)
991     """
992   try:
993     salomeFile=sys.argv[1]
994     convertedFile=sys.argv[2]
995   except :
996     print usage%(sys.argv[0])
997     sys.exit(1)
998
999   SALOMERuntime.RuntimeSALOME_setRuntime()
1000   loader=SalomeLoader()
1001
1002   try:
1003     p= loader.load(salomeFile)
1004     s= pilot.SchemaSave(p)
1005     s.save(convertedFile)
1006   except:
1007     traceback.print_exc()
1008     f=open(convertedFile,'w')
1009     f.write("<proc></proc>\n")
1010