Salome HOME
Restitution de PN_DecoupePanel
[tools/eficas.git] / Noyau / N_JDC.py
1 #@ MODIF N_JDC Noyau  DATE 04/02/2004   AUTEUR CAMBIER S.CAMBIER 
2 #            CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
5 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR   
8 # (AT YOUR OPTION) ANY LATER VERSION.                                 
9 #
10 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT 
11 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF          
12 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU    
13 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.                            
14 #
15 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE   
16 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,       
17 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.      
18 #                                                                       
19 #                                                                       
20 # ======================================================================
21 """
22    Ce module contient la classe JDC qui sert à interpréter un jeu de commandes
23 """
24
25 # Modules Python
26 import os,string,traceback
27 import types,sys,linecache
28
29 # Modules EFICAS
30 import N_OBJECT
31 import N_CR
32 from N_Exception import AsException
33 from N_ASSD import ASSD
34
35 class JDC(N_OBJECT.OBJECT):
36    """
37       Cette classe interprete un jeu de commandes fourni sous
38       la forme d'une chaine de caractères
39
40       Attributs de classe :
41
42       Attributs d'instance :
43
44    """
45    nature = "JDC"
46    CR=N_CR.CR
47    exec_init="""
48 import Accas
49 from Accas import _F
50 from Accas import *
51 NONE = None
52 """
53
54    from N_utils import SEP
55
56    def __init__(self,definition=None,procedure=None,cata=None,
57                      cata_ord_dico=None,parent=None,
58                      nom='SansNom',appli=None,context_ini=None,**args):
59       self.procedure=procedure
60       self.definition = definition
61       self.cata=cata
62       if type(self.cata) != types.TupleType and cata != None: 
63          self.cata=(self.cata,)
64       self.cata_ordonne_dico=cata_ord_dico
65       self.nom = nom
66       self.appli=appli
67       self.parent=parent
68       self.context_ini=context_ini
69       # On conserve les arguments supplémentaires. Il est possible de passer 
70       # des informations globales au JDC par ce moyen. Il pourrait etre plus 
71       # sur de mettre en place le mecanisme des mots-cles pour verifier la 
72       # validité des valeurs passées.
73       # Ceci reste à faire
74       # On initialise avec les parametres de la definition puis on 
75       # update avec ceux du JDC
76       self.args=self.definition.args
77       self.args.update(args)
78       self.nstep=0
79       self.nsd=0
80       self.par_lot='OUI'
81       if definition:
82          self.regles=definition.regles
83          self.code = definition.code
84       else:
85          self.regles=()
86          self.code = "CODE"
87       #
88       #  Creation de l objet compte rendu pour collecte des erreurs
89       #
90       self.cr = self.CR(debut = "CR phase d'initialisation", 
91                         fin = "fin CR phase d'initialisation")
92       self.g_context={}
93       # Liste pour stocker tous les concepts produits dans le JDC
94       self.sds=[]
95       # Dictionnaire pour stocker tous les concepts du JDC (acces rapide par le nom)
96       self.sds_dict={}
97       self.etapes=[]
98       self.mc_globaux={}
99       self.current_context={}
100       self.condition_context={}
101       self.index_etape_courante=0
102       self.UserError="UserError"
103       self.alea = None
104
105    def compile(self):
106       """
107          Cette methode compile la chaine procedure
108          Si des erreurs se produisent, elles sont consignées dans le 
109          compte-rendu self.cr
110       """
111       try:
112         if self.appli != None : 
113            self.appli.affiche_infos('Compilation du fichier de commandes en cours ...')
114         self.proc_compile=compile(self.procedure,self.nom,'exec')
115       except SyntaxError,e:
116         if CONTEXT.debug : traceback.print_exc()
117         l=traceback.format_exception_only(SyntaxError,e)
118         self.cr.exception("Compilation impossible : "+string.join(l))
119       return
120
121    def exec_compile(self):
122       """
123          Cette méthode execute le jeu de commandes compilé dans le contexte
124          self.g_context de l'objet JDC
125       """
126       CONTEXT.set_current_step(self)
127       # Le module nommage utilise le module linecache pour accéder
128       # au source des commandes du jeu de commandes.
129       # Dans le cas d'un fichier, on accède au contenu de ce fichier
130       # Dans le cas d'une chaine de caractères il faut accéder
131       # aux commandes qui sont dans la chaine
132       import linecache
133       linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
134       try:
135          exec self.exec_init in self.g_context
136          for obj_cata in self.cata:
137             if type(obj_cata) == types.ModuleType :
138                init2 = "from "+obj_cata.__name__+" import *"
139                exec init2 in self.g_context
140
141          # Initialisation du contexte global pour l'évaluation des conditions de BLOC
142          # On utilise une copie de l'initialisation du contexte du jdc
143          self.condition_context=self.g_context.copy()
144
145          # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
146          # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
147          # d'un autre par exemple)
148          if self.context_ini :
149             self.g_context.update(self.context_ini)
150             # Update du dictionnaire des concepts
151             for sdnom,sd in self.context_ini.items():
152                if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
153
154          if self.appli != None : 
155             self.appli.affiche_infos('Interprétation du fichier de commandes en cours ...')
156          # On sauve le contexte pour garder la memoire des constantes
157          # En mode edition (EFICAS) ou lors des verifications le contexte 
158          # est recalculé
159          # mais les constantes sont perdues
160          self.const_context=self.g_context
161          exec self.proc_compile in self.g_context
162
163          CONTEXT.unset_current_step()
164          if self.appli != None : self.appli.affiche_infos('')
165
166       except EOFError:
167         # Exception utilise pour interrompre un jeu
168         # de commandes avant la fin
169         # Fonctionnement normal, ne doit pas etre considere comme une erreur
170         CONTEXT.unset_current_step()
171
172       except AsException,e:
173         # une erreur a ete identifiee
174         if CONTEXT.debug :
175           traceback.print_exc()
176         self.cr.exception(str(e))
177         CONTEXT.unset_current_step()
178
179       except NameError,e:
180         etype, value, tb = sys.exc_info()
181         l= traceback.extract_tb(tb)
182         s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
183         message = "erreur de syntaxe,  %s ligne %d" % (s,l[-1][1])
184         if CONTEXT.debug :
185           traceback.print_exc()
186         self.cr.exception(message)
187         CONTEXT.unset_current_step()
188
189       except self.UserError,exc_val:
190         self.traiter_user_exception(exc_val)
191         CONTEXT.unset_current_step()
192     
193       except :
194         # erreur inattendue
195         # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info() 
196         # (tuple de 3 éléments)
197         if CONTEXT.debug : traceback.print_exc()
198
199         exc_typ,exc_val,exc_fr=sys.exc_info()
200         l=traceback.format_exception(exc_typ,exc_val,exc_fr)
201         self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
202                            self.nom+'\n'+ string.join(l))
203         del exc_typ,exc_val,exc_fr
204         CONTEXT.unset_current_step()
205
206    def traiter_user_exception(self,exc_val):
207        """Cette methode realise un traitement sur les exceptions utilisateur    
208           Par defaut il n'y a pas de traitement. La méthode doit etre 
209           surchargée pour en introduire un.
210        """
211        return 
212
213    def register(self,etape):
214       """
215          Cette méthode ajoute etape dans la liste des etapes : self.etapes
216          et retourne un numéro d'enregistrement
217       """
218       self.etapes.append(etape)
219       return self.g_register(etape)
220
221    def o_register(self,sd):
222       """
223          Retourne un identificateur pour concept
224       """
225       self.nsd=self.nsd+1
226       nom=sd.idracine + self.SEP + `self.nsd`
227       return nom
228
229    def g_register(self,etape):
230       """
231           Retourne un identificateur pour etape
232       """
233       self.nstep=self.nstep+1
234       idetape=etape.idracine + self.SEP + `self.nstep`
235       return idetape
236
237    def create_sdprod(self,etape,nomsd):
238       """ 
239           Intention : Cette methode doit fabriquer le concept produit retourne
240                   par l'etape etape et le nommer.
241                   Elle est appelée à l'initiative de l'etape
242                   pendant le processus de construction de cette etape : 
243                     methode __call__ de la classe CMD (OPER ou MACRO)
244                   Ce travail est réalisé par le contexte supérieur 
245                   (etape.parent) car dans certains cas, le concept ne doit 
246                   pas etre fabriqué mais l'etape doit simplement utiliser 
247                   un concept préexistant.
248                   Cas 1 : etape.reuse != None : le concept est réutilisé
249                   Cas 2 : l'étape appartient à une macro qui a déclaré un 
250                           concept de sortie qui doit etre produit par cette 
251                           etape.
252                   Dans le cas du JDC, le deuxième cas ne peut pas se produire.
253       """
254       sd= etape.get_sd_prod()
255       if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
256          # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation 
257          # d un concept. Commande non reentrante ou reuse absent.
258          self.NommerSdprod(sd,nomsd)
259       return sd
260
261    def NommerSdprod(self,sd,sdnom,restrict='non'):
262       """ 
263           Nomme la SD apres avoir verifie que le nommage est possible : nom 
264           non utilise
265           Si le nom est deja utilise, leve une exception
266           Met le concept créé dans le concept global g_context
267       """
268       if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
269
270       o=self.sds_dict.get(sdnom,None)
271       if isinstance(o,ASSD):
272          raise AsException("Nom de concept deja defini : %s" % sdnom)
273
274       # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
275       # Ajoute a la creation (appel de reg_sd).
276       self.sds_dict[sdnom]=sd
277       sd.nom=sdnom
278
279       # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
280       if restrict == 'non':
281          self.g_context[sdnom]=sd
282
283    def reg_sd(self,sd):
284       """ 
285           Methode appelee dans l __init__ d un ASSD lors de sa creation 
286           pour s enregistrer
287       """
288       self.sds.append(sd)
289       return self.o_register(sd)
290
291    def delete_concept_after_etape(self,etape,sd):
292       """
293           Met à jour les étapes du JDC qui sont après etape suite à
294           la disparition du concept sd
295       """
296       # Cette methode est définie dans le noyau mais ne sert que pendant 
297       # la phase de creation des etapes et des concepts. Il n'y a aucun 
298       # traitement particulier à réaliser.
299       # Dans d'autres conditions, il faut surcharger cette méthode
300       return
301
302    def supprime(self):
303       N_OBJECT.OBJECT.supprime(self)
304       for etape in self.etapes:
305          etape.supprime()
306
307    def get_file(self,unite=None,fic_origine=''):
308       """
309           Retourne le nom du fichier correspondant à un numero d'unité 
310           logique (entier) ainsi que le source contenu dans le fichier
311       """
312       if self.appli :
313          # Si le JDC est relié à une application maitre, on délègue la recherche
314          file,text= self.appli.get_file(unite,fic_origine)
315       else:
316          file = None
317          if unite != None:
318             if os.path.exists("fort."+str(unite)):
319                file= "fort."+str(unite)
320          if file == None :
321             raise AsException("Impossible de trouver le fichier correspondant"
322                                " a l unite %s" % unite)
323          if not os.path.exists(file):
324             raise AsException("%s n'est pas un fichier existant" % unite)
325          fproc=open(file,'r')
326          text=fproc.read()
327          fproc.close()
328       if file == None : return None,None
329       text=string.replace(text,'\r\n','\n')
330       linecache.cache[file]=0,0,string.split(text,'\n'),file
331       return file,text
332
333    def set_par_lot(self,par_lot):
334       """ 
335           Met le mode de traitement a PAR LOT 
336           ou a COMMANDE par COMMANDE
337           en fonction de la valeur du mot cle PAR_LOT et 
338           du contexte : application maitre ou pas
339       """
340       if self.appli == None:
341         # Pas d application maitre
342         self.par_lot=par_lot
343       else:
344         # Avec application maitre
345         self.par_lot='OUI'
346
347    def accept(self,visitor):
348       """
349          Cette methode permet de parcourir l'arborescence des objets
350          en utilisant le pattern VISITEUR
351       """
352       visitor.visitJDC(self)
353
354    def interact(self):
355       """
356           Cette methode a pour fonction d'ouvrir un interpreteur 
357           pour que l'utilisateur entre des commandes interactivement
358       """
359       CONTEXT.set_current_step(self)
360       try:
361          # Le module nommage utilise le module linecache pour accéder
362          # au source des commandes du jeu de commandes.
363          # Dans le cas d'un fichier, on accède au contenu de ce fichier
364          # Dans le cas de la console interactive, il faut pouvoir accéder
365          # aux commandes qui sont dans le buffer de la console
366          import linecache,code
367          console= code.InteractiveConsole(self.g_context,filename="<console>")
368          linecache.cache["<console>"]=0,0,console.buffer,"<console>"
369          banner="""***********************************************
370 *          Interpreteur interactif %s
371 ***********************************************""" % self.code
372          console.interact(banner)
373       finally:
374          console=None
375          CONTEXT.unset_current_step()
376
377    def get_contexte_avant(self,etape):
378       """
379          Retourne le dictionnaire des concepts connus avant etape
380          On tient compte des commandes qui modifient le contexte
381          comme DETRUIRE ou les macros
382          Si etape == None, on retourne le contexte en fin de JDC
383       """
384       # L'étape courante pour laquelle le contexte a été calculé est 
385       # mémorisée dans self.index_etape_courante
386       # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en 
387       # mémorisant l'étape
388       # courante pendant le processus de construction des étapes.
389       # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
390       # remettre ce pointeur à 0
391       if etape:
392          index_etape=self.etapes.index(etape)
393       else:
394          index_etape=len(self.etapes)
395       if index_etape >= self.index_etape_courante:
396          # On calcule le contexte en partant du contexte existant
397          d=self.current_context
398          if self.index_etape_courante==0 and self.context_ini:
399             d.update(self.context_ini)
400          liste_etapes=self.etapes[self.index_etape_courante:index_etape]
401       else:
402          d=self.current_context={}
403          if self.context_ini:d.update(self.context_ini)
404          liste_etapes=self.etapes
405
406       for e in liste_etapes:
407          if e is etape:
408             break
409          if e.isactif():
410             e.update_context(d)
411       self.index_etape_courante=index_etape
412       return d
413
414    def get_global_contexte(self):
415       return self.g_context.copy()
416
417    def get_cmd(self,nomcmd):
418       """
419           Méthode pour recuperer la definition d'une commande
420           donnee par son nom dans les catalogues declares
421           au niveau du jdc
422       """
423       for cata in self.cata:
424           if hasattr(cata,nomcmd):
425              return getattr(cata,nomcmd)
426