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.
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.
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
28 # protection pour eficas
31 from Messages.context_info import message_context_concept
38 # -----------------------------------------------------------------------------
39 contacter_assistance = """
40 Il y a probablement une erreur dans la programmation.
41 Veuillez contacter votre assistance technique."""
43 # -----------------------------------------------------------------------------
44 # -----------------------------------------------------------------------------
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
55 # est-ce qu'une erreur <E> s'est produite
58 # compteur des alarmes émises { 'id_alarm' : count }
61 # on prépare le dictionnaire des valeurs par défaut des arguments (dicarg) :
62 self.default_args = {}
63 # initialisation des 10 premiers
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'
69 # -----------------------------------------------------------------------------
70 def __call__(self, *args, **kwargs):
71 """Raccourci pour simplifier l'appel depuis astermodule.c et UTMESS.
73 self.print_message(*args, **kwargs)
75 # -----------------------------------------------------------------------------
76 def print_message(self, code, idmess, valk=(), vali=(), valr=(),
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.
86 # récupération du texte du message
87 dictmess = self.get_message(code, idmess, valk, vali, valr)
89 # on le met dans le buffer
90 self.add_to_buffer(dictmess)
92 # si on n'attend pas une suite, ...
93 if len(code) < 2 or code[1] != '+':
94 # mise à jour des compteurs
97 # on imprime le message en attente
98 self.print_buffer_content()
101 reason = ' <EXCEPTION LEVEE> %s' % idmess
103 raise aster.error, reason
105 raise aster.FatalError, reason
109 # -----------------------------------------------------------------------------
110 def build_dict_args(self, valk, vali, valr):
111 """Construit le dictionnaire de formattage du message.
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]
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)
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'
135 dicarg = self.build_dict_args(valk, vali, valr)
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()
143 assert numess > 0 and numess < 100, idmess
145 # import catamess => cata_msg
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)))
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']
164 fmt_msg = cata_msg[numess]
169 'id_message' : idmess,
170 'corps_message' : fmt_msg % dicarg,
171 'context_info' : self.get_context(ctxt_msg, idmess, dicarg),
174 dictmess['id_message'] = ''
175 except Exception, msg:
179 'corps_message' : """Erreur de programmation.
180 Le message %s n'a pas pu etre formaté correctement.
181 --------------------------------------------------------------------------
183 --------------------------------------------------------------------------
187 ''.join(traceback.format_tb(sys.exc_traceback)), contacter_assistance),
190 # limite la longueur des ligness
191 dictmess['corps_message'] = cut_long_lines(dictmess['corps_message'], 80)
194 # -----------------------------------------------------------------------------
195 def init_buffer(self):
196 """Initialise le buffer.
200 # -----------------------------------------------------------------------------
201 def add_to_buffer(self, dictmess):
202 """Ajoute le message décrit dans le buffer en vue d'une impression
205 self._buffer.append(dictmess)
207 # -----------------------------------------------------------------------------
208 def get_current_code(self):
209 """Retourne le code du message du buffer = code du message le plus grave
212 dgrav = { '?' : -9, 'I' : 0, 'A' : 1, 'S' : 4, 'Z' : 4, 'E' : 6, 'F' : 10 }
215 for dictmess in self._buffer:
216 code = dictmess['code'][0]
217 if dgrav.get(code, -9) > dgrav.get(current, -9):
222 # -----------------------------------------------------------------------------
223 def get_current_id(self):
224 """Retourne l'id du message du buffer = id du premier message
226 return self._buffer[0]['id_message']
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.
236 if len(self._buffer) < 1:
239 # construction du dictionnaire du message global
241 'code' : self.get_current_code(),
242 'id_message' : self.get_current_id(),
243 'liste_message' : [],
244 'liste_context' : [],
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'])
252 # liste des unités d'impression en fonction du type de message
253 l_unit = self.list_unit(dglob['code'])
255 # texte final et impression
256 txt = self.format_message(dglob)
258 aster.affiche(unite, txt)
262 # -----------------------------------------------------------------------------
263 def update_counter(self):
264 """Mise à jour des compteurs et réaction si besoin.
267 code = self.get_current_code()
271 self.erreur_E = False
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
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.
298 self.erreur_E = False
300 self.print_message('F', 'SUPERVIS_6', exception=True)
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
310 charh = '-' # horizontal
311 charv = '!' # vertical
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."""),
325 'header' : """<%(type_message)s> %(str_id_message)s""",
326 'ligne' : '%(charv)s %%-%(maxlen)ds %(charv)s',
327 'corps' : """%(header)s
341 # format light pour les infos
344 'header' : """<%(type_message)s> """,
346 'corps' : """%(corps_message)s
348 'final' : """%(corps)s""",
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']
355 dmsg['str_id_message'] = ''
358 format = format_general
359 if dmsg['type_message'] == 'I':
360 format = format_light
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
367 dmsg['corps'] = format['corps'] % dmsg
370 # longueur de la ligne la plus longue
371 l_line = dmsg['corps'].splitlines()
372 maxlen = max([len(line) for line in l_line])
374 # format des lignes sur maxlen caractères
381 fmt_line = format['ligne'] % dlin
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
389 newtxt = format['final'] % dmsg
391 l_txt = [format['decal'] + line for line in newtxt.splitlines()]
393 return clean_string(os.linesep.join(l_txt))
395 # -----------------------------------------------------------------------------
396 def list_unit(self, code):
397 """Retourne la liste des noms de fichiers (logiques) sur lesquels doit
398 etre imprimé le message.
400 #IDF = INDEX('EFIDASXZ', ...)
401 #'Z' (IDF=8) = LEVEE D'EXCEPTION
403 'E' : ('ERREUR', 'MESSAGE', 'RESULTAT'),
405 'A' : ('MESSAGE', 'RESULTAT'),
407 d['F'] = d['S'] = d['Z'] = d['E']
409 return d.get(code, d['F'])
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
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'
426 # -----------------------------------------------------------------------------
427 def get_context(self, ctxt_msg, idmess, dicarg):
428 """Prise en compte du context du message pour donner d'autres infos
430 ctxt_msg est un dictionnaire. Les clés traitées sont :
436 # tout dans un try/except car c'est du bonus, il ne faudrait pas planter !
438 if ctxt_msg.has_key('CONCEPT'):
439 l_co = [dicarg[arg] for arg in force_enum(ctxt_msg['CONCEPT'])]
441 msg.append(message_context_concept(co))
444 return os.linesep.join(msg)
447 # -----------------------------------------------------------------------------
448 # -----------------------------------------------------------------------------
449 def clean_string(chaine):
450 """Supprime tous les caractères non imprimables.
461 # -----------------------------------------------------------------------------
463 """Retourne `obj` si c'est une liste ou un tuple,
464 sinon retourne [obj,]
466 if type(obj) not in (list, tuple):
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`.
476 while len(l_fields) > 0:
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 != '']
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`
488 On utilise successivement les séparateurs de `l_separ`.
490 l_lines = txt.split(sep)
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)
499 newlines.append(line)
500 # au plus haut niveau, on assemble le texte
501 if sep == os.linesep:
502 newlines = os.linesep.join(newlines)
505 # -----------------------------------------------------------------------------
506 # -----------------------------------------------------------------------------
507 # unique instance du MESSAGE_LOGGER
508 MessageLog = MESSAGE_LOGGER()
511 # -----------------------------------------------------------------------------
512 def UTMESS(code, idmess, valk=(), vali=(), valr=()):
513 """Utilitaire analogue à la routine fortran U2MESS/U2MESG avec les arguments
515 code : 'A', 'E', 'S', 'F', 'I'
516 idmess : identificateur du message
517 valk, vali, valr : liste des chaines, entiers ou réels.
519 Appel sans valeurs : avec valeurs :
520 UTMESS('A', 'SUPERVIS_55') UTMESS('A', 'SUPERVIS_55', vali=[1, 2])
523 - Nommer les arguments permet de ne pas tous les passer.
524 - Meme fonctionnement que U2MESG :
526 + puis exception ou abort en fonction du niveau d'erreur.
528 MessageLog(code, idmess, valk, vali, valr, exception=True)