]> SALOME platform Git repositories - tools/eficas.git/blob - Aster/Cata/Utilitai/Utmess.py
Salome HOME
CCAR: merge de la version 1.14 dans la branche principale
[tools/eficas.git] / Aster / Cata / Utilitai / Utmess.py
1 #@ MODIF Utmess Utilitai  DATE 16/10/2007   AUTEUR REZETTE C.REZETTE 
2 # -*- coding: iso-8859-1 -*-
3 #            CONFIGURATION MANAGEMENT OF EDF VERSION
4 # ======================================================================
5 # COPYRIGHT (C) 1991 - 2004  EDF R&D                  WWW.CODE-ASTER.ORG
6 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY  
7 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY  
8 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR     
9 # (AT YOUR OPTION) ANY LATER VERSION.                                                  
10 #                                                                       
11 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT   
12 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF            
13 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU      
14 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.                              
15 #                                                                       
16 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE     
17 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,         
18 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.        
19 # ======================================================================
20 # RESPONSABLE COURTOIS M.COURTOIS
21
22 import os
23 import sys
24 import traceback
25 import imp
26 import re
27
28 # protection pour eficas
29 try:
30    import aster
31    from Messages.context_info import message_context_concept
32 except:
33    pass
34
35 def _(s):
36    return s
37
38 # -----------------------------------------------------------------------------
39 contacter_assistance = """
40 Il y a probablement une erreur dans la programmation.
41 Veuillez contacter votre assistance technique."""
42
43 # -----------------------------------------------------------------------------
44 # -----------------------------------------------------------------------------
45 class MESSAGE_LOGGER:
46    """Classe gérant l'impression de messages.
47    On ne crée qu'une instance de ce type.
48    Cette instance est accessible via le module E_Global pour astermodule.c
49    """
50    def __init__(self):
51       """Initialisation
52       """
53       self.init_buffer()
54       
55       # est-ce qu'une erreur <E> s'est produite
56       self.erreur_E = False
57       
58       # compteur des alarmes émises { 'id_alarm' : count }
59       self.count_alarm = {}
60       
61       # on prépare le dictionnaire des valeurs par défaut des arguments (dicarg) :
62       self.default_args = {}
63       # initialisation des 10 premiers
64       for i in range(1,11):
65          self.default_args['i%d' % i] = 99999999
66          self.default_args['r%d' % i] = 9.9999E99
67          self.default_args['k%d' % i] = 'xxxxxx'
68
69 # -----------------------------------------------------------------------------
70    def __call__(self, *args, **kwargs):
71       """Raccourci pour simplifier l'appel depuis astermodule.c et UTMESS.
72       """
73       self.print_message(*args, **kwargs)
74
75 # -----------------------------------------------------------------------------
76    def print_message(self, code, idmess, valk=(), vali=(), valr=(),
77                      exception=False):
78       """Appelé par la routine fortran U2MESG ou à la fonction python UTMESS
79       pour afficher un message.
80       L'impression de ce message est différée si le `code` est suivi d'un "+".
81          code  : 'A', 'E', 'S', 'F', 'I'
82          idmess : identificateur du message
83          valk, vali, valr : liste des chaines, entiers ou réels.
84       Si exception==True, on lève une exception en cas d'erreur.
85       """
86       # récupération du texte du message
87       dictmess = self.get_message(code, idmess, valk, vali, valr)
88       
89       # on le met dans le buffer
90       self.add_to_buffer(dictmess)
91       
92       # si on n'attend pas une suite, ...
93       if len(code) < 2 or code[1] != '+':
94          # mise à jour des compteurs
95          self.update_counter()
96          
97          # on imprime le message en attente
98          self.print_buffer_content()
99
100          if exception:
101             reason = ' <EXCEPTION LEVEE> %s' % idmess
102             if code[0] == 'S':
103                raise aster.error, reason
104             elif code[0] == 'F':
105                raise aster.FatalError, reason
106       
107       return None
108
109 # -----------------------------------------------------------------------------
110    def build_dict_args(self, valk, vali, valr):
111       """Construit le dictionnaire de formattage du message.
112       """
113       # homogénéisation : uniquement des tuples + strip des chaines de caractères
114       valk, vali, valr = map(force_enum, (valk, vali, valr))
115       valk    = [k.strip() for k in valk]
116       
117       # variables passées au message
118       dicarg = self.default_args.copy()
119       for i in range(1,len(valk)+1):
120          dicarg['k%d' % i] = valk[i-1]
121       for i in range(1,len(vali)+1):
122          dicarg['i%d' % i] = vali[i-1]
123       for i in range(1,len(valr)+1):
124          dicarg['r%d' % i] = valr[i-1]
125       # valeur spéciale : ktout = concaténation de toutes les chaines
126       dicarg['ktout'] = ' '.join(valk)
127    
128       return dicarg
129
130 # -----------------------------------------------------------------------------
131    def get_message(self, code, idmess, valk=(), vali=(), valr=()):
132       """Retourne le texte du message dans un dictionnaire dont les clés sont :
133          'code', 'id_message', 'corps_message'
134       """
135       dicarg = self.build_dict_args(valk, vali, valr)
136    
137       # décodage : idmess => (catamess, numess)
138       idmess  = idmess.strip()
139       x = idmess.split("_")
140       assert len(x) > 1, idmess
141       catamess='_'.join(x[0:-1]).lower()
142       numess = int(x[-1])
143       assert numess > 0 and numess < 100, idmess
144    
145       # import catamess => cata_msg
146       try:
147          mod = __import__('Messages.%s' % catamess, globals(), locals(), [catamess])
148          # si le dictionnaire n'existe pas, on alertera au moment du formatage.
149          cata_msg = getattr(mod, 'cata_msg', {})
150       except Exception, msg:
151          # doit permettre d'éviter la récursivité
152          if catamess != 'supervis':
153             self.print_message('A', 'SUPERVIS_57', valk=(catamess, str(msg)))
154          cata_msg = {}
155       
156       # corps du message
157       try:
158          # cata_msg[num] = 'format'
159          #              ou {'message' : 'format', 'context' : 'éléments de contexte'}
160          if type(cata_msg[numess]) == dict:
161             fmt_msg  = cata_msg[numess]['message']
162             ctxt_msg = cata_msg[numess]['context']
163          else:
164             fmt_msg  = cata_msg[numess]
165             ctxt_msg = None
166          
167          dictmess = {
168             'code'          : code,
169             'id_message'    : idmess,
170             'corps_message' : fmt_msg % dicarg,
171             'context_info'  : self.get_context(ctxt_msg, idmess, dicarg),
172          }
173          if code == 'I':
174             dictmess['id_message'] = ''
175       except Exception, msg:
176          dictmess = {
177             'code'          : code,
178             'id_message'    : '',
179             'corps_message' : """Erreur de programmation.
180 Le message %s n'a pas pu etre formaté correctement.
181 --------------------------------------------------------------------------
182 %s
183 --------------------------------------------------------------------------
184
185 %s""" \
186       % (idmess,
187          ''.join(traceback.format_tb(sys.exc_traceback)), contacter_assistance),
188             'context_info'  : '',
189          }
190       # limite la longueur des ligness
191       dictmess['corps_message'] = cut_long_lines(dictmess['corps_message'], 80)
192       return dictmess
193
194 # -----------------------------------------------------------------------------
195    def init_buffer(self):
196       """Initialise le buffer.
197       """
198       self._buffer = []
199
200 # -----------------------------------------------------------------------------
201    def add_to_buffer(self, dictmess):
202       """Ajoute le message décrit dans le buffer en vue d'une impression
203       ultérieure.
204       """
205       self._buffer.append(dictmess)
206
207 # -----------------------------------------------------------------------------
208    def get_current_code(self):
209       """Retourne le code du message du buffer = code du message le plus grave
210       (cf. dgrav)
211       """
212       dgrav = { '?' : -9, 'I' : 0, 'A' : 1, 'S' : 4, 'Z' : 4, 'E' : 6, 'F' : 10 }
213       
214       current = '?'
215       for dictmess in self._buffer:
216          code = dictmess['code'][0]
217          if dgrav.get(code, -9) > dgrav.get(current, -9):
218             current = code
219       
220       return current
221
222 # -----------------------------------------------------------------------------
223    def get_current_id(self):
224       """Retourne l'id du message du buffer = id du premier message
225       """
226       return self._buffer[0]['id_message']
227
228 # -----------------------------------------------------------------------------
229    def print_buffer_content(self):
230       """Extrait l'ensemble des messages du buffer dans un dictionnaire unique,
231       imprime le message, et vide le buffer pour le message suivant.
232          - code : celui du message le plus grave (cf. dgrav)
233          - id   : celui du premier message qui est affiché
234          - corps : concaténation de tous les messages.
235       """
236       if len(self._buffer) < 1:
237          return None
238       
239       # construction du dictionnaire du message global
240       dglob = {
241          'code'          : self.get_current_code(),
242          'id_message'    : self.get_current_id(),
243          'liste_message' : [],
244          'liste_context' : [],
245       }
246       for dictmess in self._buffer:
247          dglob['liste_message'].append(dictmess['corps_message'])
248          dglob['liste_context'].append(dictmess['context_info'])
249       dglob['corps_message'] = ''.join(dglob['liste_message'])
250       dglob['context_info'] = ''.join(dglob['liste_context'])
251       
252       # liste des unités d'impression en fonction du type de message
253       l_unit = self.list_unit(dglob['code'])
254       
255       # texte final et impression
256       txt = self.format_message(dglob)
257       for unite in l_unit:
258          aster.affiche(unite, txt)
259       
260       self.init_buffer()
261
262 # -----------------------------------------------------------------------------
263    def update_counter(self):
264       """Mise à jour des compteurs et réaction si besoin.
265       """
266       nmax_alarm = 5
267       code = self.get_current_code()
268       if   code == 'E':
269          self.erreur_E = True
270       elif code == 'F':
271          self.erreur_E = False
272       elif code == 'A':
273          idmess = self.get_current_id()
274          # nombre d'occurence de cette alarme
275          self.count_alarm[idmess] = self.count_alarm.get(idmess, 0) + 1
276          if self.count_alarm[idmess] == nmax_alarm:
277             # Pour mettre en relief le message SUPERVIS_41, on le sépare
278             # de la dernière alarme
279             self.print_buffer_content()
280             dictmess = self.get_message(code, 'SUPERVIS_41',
281                                         valk=idmess, vali=nmax_alarm)
282             self.add_to_buffer(dictmess)
283          elif self.count_alarm[idmess] > nmax_alarm:
284             # count_alarm > 5, on vide le buffer
285             self.init_buffer()
286
287 # -----------------------------------------------------------------------------
288    def check_counter(self, silent=False):
289       """Méthode "jusqu'ici tout va bien" !
290       Si des erreurs <E> se sont produites, on arrete le code en <F>.
291       Appelée par FIN ou directement au cours de l'exécution d'une commande.
292       Retourne un entier : 0 si tout est ok.
293       Si silent==True, on n'émet pas de message, on ne s'arrete pas.
294       """
295       iret = 0
296       if self.erreur_E:
297          iret = 4
298          self.erreur_E = False
299          if not silent:
300             self.print_message('F', 'SUPERVIS_6', exception=True)
301       return iret
302
303 # -----------------------------------------------------------------------------
304    def format_message(self, dictmess):
305       """Formate le message décrit dans un dico :
306          'code'          : A, E, S, F, I
307          'id_message'    : identification du message
308          'corps_message' : texte
309       """
310       charh = '-'    # horizontal
311       charv = '!'    # vertical
312       charc = '!'    # coin
313       dcomm = {
314          'A' : _("""Ceci est une alarme. Si vous ne comprenez pas le sens de cette
315 alarme, vous pouvez obtenir des résultats inattendus !"""),
316          'E' : _("""Cette erreur sera suivie d'une erreur fatale."""),
317          'S' : _("""Cette erreur est fatale. Le code s'arrete. Toutes les étapes
318 du calcul ont été sauvées dans la base jusqu'au moment de l'arret."""),
319          'F' : _("""Cette erreur est fatale. Le code s'arrete."""),
320       }
321       
322       # format complet
323       format_general = {
324          'decal'  : '   ',
325          'header' : """<%(type_message)s> %(str_id_message)s""",
326          'ligne'  : '%(charv)s %%-%(maxlen)ds %(charv)s',
327          'corps'  : """%(header)s
328
329 %(corps_message)s
330 %(context_info)s
331
332 %(commentaire)s
333 """,
334          'final'  : """
335 %(separateur)s
336 %(corps)s
337 %(separateur)s
338
339 """,
340       }
341       # format light pour les infos
342       format_light = {
343          'decal'  : '',
344          'header' : """<%(type_message)s> """,
345          'ligne'  : '%%s',
346          'corps'  : """%(corps_message)s
347 %(context_info)s""",
348          'final'  : """%(corps)s""",
349       }
350       dmsg = dictmess.copy()
351       dmsg['type_message'] = self.get_type_message(dictmess['code'])
352       if dmsg['id_message'] != '':
353          dmsg['str_id_message'] = '<%s>' % dmsg['id_message']
354       else:
355          dmsg['str_id_message'] = ''
356       
357       # format utilisé
358       format = format_general
359       if dmsg['type_message'] == 'I':
360          format = format_light
361       
362       dmsg['header']      = format['header'] % dmsg
363       dmsg['commentaire'] = dcomm.get(dmsg['type_message'], '')
364       if re.search('^DVP', dmsg['id_message']) != None:
365          dmsg['commentaire'] += contacter_assistance
366       
367       dmsg['corps']       = format['corps'] % dmsg
368       
369       
370       # longueur de la ligne la plus longue
371       l_line = dmsg['corps'].splitlines()
372       maxlen = max([len(line) for line in l_line])
373       
374       # format des lignes sur maxlen caractères
375       dlin = {
376          'charh'  : charh,
377          'charv'  : charv,
378          'charc'  : charc,
379          'maxlen' : maxlen
380       }
381       fmt_line = format['ligne'] % dlin
382       
383       # on formate toutes les lignes
384       txt = [fmt_line % line for line in l_line]
385       dmsg['corps'] = os.linesep.join(txt)
386       dmsg['separateur'] = charc + charh * (maxlen + 2) + charc
387       
388       # ligne haut et bas
389       newtxt = format['final'] % dmsg
390       # on décale
391       l_txt = [format['decal'] + line for line in newtxt.splitlines()]
392       
393       return clean_string(os.linesep.join(l_txt))
394
395 # -----------------------------------------------------------------------------
396    def list_unit(self, code):
397       """Retourne la liste des noms de fichiers (logiques) sur lesquels doit
398       etre imprimé le message.
399       """
400       #IDF  = INDEX('EFIDASXZ', ...)
401       #'Z' (IDF=8) = LEVEE D'EXCEPTION
402       d = {
403          'E' : ('ERREUR', 'MESSAGE', 'RESULTAT'),
404          'I' : ('MESSAGE',),
405          'A' : ('MESSAGE', 'RESULTAT'),
406       }
407       d['F'] = d['S'] = d['Z'] = d['E']
408       d['X'] = d['A']
409       return d.get(code, d['F'])
410
411 # -----------------------------------------------------------------------------
412    def get_type_message(self, code):
413       """Retourne le type du message affiché.
414       En cas d'erreur, si on lève une exception au lieu de s'arreter,
415       on n'affiche pas le type de l'erreur pour ne pas fausser le diagnostic
416       """
417       typmess = code.strip()
418       if aster.onFatalError() == 'EXCEPTION':
419          if typmess in ('E', 'F'):
420             typmess = 'EXCEPTION'
421       # dans tous les cas, pour S et Z (exception), on affiche EXCEPTION.
422       if typmess in ('Z', 'S'):
423          typmess = 'EXCEPTION'
424       return typmess
425
426 # -----------------------------------------------------------------------------
427    def get_context(self, ctxt_msg, idmess, dicarg):
428       """Prise en compte du context du message pour donner d'autres infos
429       à l'utilisateur.
430       ctxt_msg est un dictionnaire. Les clés traitées sont :
431          - CONCEPT
432       """
433       if not ctxt_msg:
434          return ''
435       msg = []
436       # tout dans un try/except car c'est du bonus, il ne faudrait pas planter !
437       try:
438          if ctxt_msg.has_key('CONCEPT'):
439             l_co =  [dicarg[arg] for arg in force_enum(ctxt_msg['CONCEPT'])]
440             for co in l_co:
441                msg.append(message_context_concept(co))
442       except:
443          pass
444       return os.linesep.join(msg)
445
446
447 # -----------------------------------------------------------------------------
448 # -----------------------------------------------------------------------------
449 def clean_string(chaine):
450    """Supprime tous les caractères non imprimables.
451    """
452    invalid = '?'
453    txt = []
454    for c in chaine:
455       if ord(c) != 0:
456          txt.append(c)
457       else:
458          txt.append(invalid)
459    return ''.join(txt)
460
461 # -----------------------------------------------------------------------------
462 def force_enum(obj):
463    """Retourne `obj` si c'est une liste ou un tuple,
464    sinon retourne [obj,]
465    """
466    if type(obj) not in (list, tuple):
467       obj = [obj,]
468    return obj
469
470 # -----------------------------------------------------------------------------
471 def maximize_lines(l_fields, maxlen, sep):
472    """Construit des lignes dont la longueur est au plus de `maxlen` caractères.
473    Les champs sont assemblés avec le séparateur `sep`.
474    """
475    newlines = []
476    while len(l_fields) > 0:
477       cur = []
478       while len(l_fields) > 0 and len(sep.join(cur + [l_fields[0],])) <= maxlen:
479          cur.append(l_fields.pop(0))
480       newlines.append(sep.join(cur))
481    newlines = [l for l in newlines if l != '']
482    return newlines
483
484 def cut_long_lines(txt, maxlen, sep=os.linesep,
485                    l_separ=(' ', ',', ';', '.', ':')):
486    """Coupe les morceaux de `txt` (isolés avec `sep`) de plus de `maxlen`
487    caractères.
488    On utilise successivement les séparateurs de `l_separ`.
489    """
490    l_lines = txt.split(sep)
491    newlines = []
492    for line in l_lines:
493       if len(line) > maxlen:
494          l_sep = list(l_separ)
495          line = cut_long_lines(line, maxlen, l_sep[0], l_sep[1:])
496          line = maximize_lines(line, maxlen, l_sep[0])
497          newlines.extend(line)
498       else:
499          newlines.append(line)
500    # au plus haut niveau, on assemble le texte
501    if sep == os.linesep:
502       newlines = os.linesep.join(newlines)
503    return newlines
504
505 # -----------------------------------------------------------------------------
506 # -----------------------------------------------------------------------------
507 # unique instance du MESSAGE_LOGGER
508 MessageLog = MESSAGE_LOGGER()
509
510
511 # -----------------------------------------------------------------------------
512 def UTMESS(code, idmess, valk=(), vali=(), valr=()):
513    """Utilitaire analogue à la routine fortran U2MESS/U2MESG avec les arguments
514    optionnels.
515       code   : 'A', 'E', 'S', 'F', 'I'
516       idmess : identificateur du message
517       valk, vali, valr : liste des chaines, entiers ou réels.
518    
519    Appel sans valeurs :                avec valeurs :
520       UTMESS('A', 'SUPERVIS_55')          UTMESS('A', 'SUPERVIS_55', vali=[1, 2])
521    
522    Remarques :
523       - Nommer les arguments permet de ne pas tous les passer.
524       - Meme fonctionnement que U2MESG :
525          + appel à MessageLog
526          + puis exception ou abort en fonction du niveau d'erreur.
527    """
528    MessageLog(code, idmess, valk, vali, valr, exception=True)
529
530