#!/usr/bin/python
# coding: iso-8859-15

global traceback
import traceback
global sys
import sys
global thread
import thread
#//LIB_START
global time
import time
#//LIB_END

## @brief Package für Zugriff auf die Debug-Seite des HS/FS.
class hsl20_4_debug_page:

    ## @brief Zugriff auf die Debug-Seite des HS/FS.
    ## @details
    ## Eine Instanz dieser Klasse bietet die Möglichkeit, Informationen auf der Debug-Seite des HS/FS auszugeben.
    ## Durch einen Aufruf der Methode hsl20_4.framework#create_debug_section wird auf der Debug-Seite automatisch eine eigene Sektion angelegt.
    ## Alle Methoden, die diese Klasse anbietet, beziehen sich immer auf diese Sektion.
    ## @note Pro Modul-Kontext gibt es eine Sektion auf der Debug-Seite.
    ##
    ## Ein Code-Beispiel dafür gibt es auf in der Dokumentation zum @b Remanentspeicher.
    ## @if OLD_CHANGELOG
    ## @chlg02 Umbenannt: DebugPage -> Section
    ## @chlg03 add_value_to_list wurde entfernt. (Ersetzt durch add_message)
    ## @endif
    class Section:

        ## Konstruktor.
        ##
        ## @warning Diese Klasse sollte nicht direkt instanziert werden.
        ##
        ## @param section_key @e string @n Schlüssel, unter dem alle Einträge auf der Debug-Seite zusammengefasst werden.
        def __init__(self, section_key):
            ## @cond NO_DOC
            self.__context_id = section_key
            self.__fields = {}
            self.__exceptions = []
            self.__messages = []
            self.__callbacks = []
            self.__lock = thread.allocate_lock()
            ## @endcond


        ## Setzt einen Wert. Gibt es den übergebenen Schlüssel noch nicht, wird er automatisch angelegt.
        ## @param key @e string @n Schlüssel
        ## @param value @e string @n Wert
        def set_value(self, key, value):
            ## @cond NO_DOC
            with self.__lock:
                self.__fields[key] = value
            ## @endcond


        ## Setzt einen Wert. Berechnet anhand des neuen und des vorhandenen Werts den Durchschnittswert.
        ## Gibt es den übergebenen Schlüssel noch nicht, wird er automatisch angelegt.
        ## @param key @e string @n Schlüssel
        ## @param value @e string @n Wert
        def add_value_to_average_field(self, key, value):
            ## @cond NO_DOC
            with self.__lock:
                if not self.__fields.has_key(key):
                    self.__fields[key] = float(value)
                else:
                    self.__fields[key] = float((self.__fields[key] + value) / 2)
            ## @endcond


        ## Erhöht einen Wert um @e '1'. Gibt es den übergebenen Schlüssel noch nicht, wird er automatisch angelegt und auf @e '1' gesetzt. @n
        ## @note Beim Erhöhen kann auch ein Schlüssel angegeben werden, der mit einer anderen Methode dieser Klasse erzeugt wurde.
        ## @param key @e string @n Schlüssel
        ## @if OLD_CHANGELOG
        ## @chlg04 Anmerkung in der Beschreibung hinzugefügt
        ## @endif
        def increase_counter_field(self, key):
            ## @cond NO_DOC
            with self.__lock:
                if not self.__fields.has_key(key):
                    self.__fields[key] = 1
                else:
                    self.__fields[key] += 1
            ## @endcond


        ## Fügt der Debug-Sektion eine Meldungsliste hinzu, sofern noch keine Meldungsliste vorhanden ist. @n
        ## Es werden bis zu 25 unterschiedliche Meldungen angezeigt.
        ## Bei Überlauf verschwindet der älteste Eintrag. @n
        ## Die Darstellung einer Meldung: @n
        ##     ZEITSTEMPEL (ANZAHL) @n
        ##     MELDUNGSTEXT @n
        ## @note Wird die gleiche Meldung mehrfach hinzugefügt, wird sie nur einmal dargestellt und mit einem Zähler versehen.
        ## @param message @e string @n Meldungstext
        ## @chg20_04 Hinweis zur Meldungsliste ergänzt
        ## @if OLD_CHANGELOG
        ## @chlg03 Neu
        ## @endif
        def add_message(self, message):
            ## @cond NO_DOC
            with self.__lock:
                for item in self.__messages:
                    if item[1]==message:
                        item[0] = time.time()
                        item[2]+=1
                        return
                self.__messages.append([time.time(),message,1])
                self.__messages.sort()
                if len(self.__messages)>25:
                    del self.__messages[0]
            ## @endcond


        ## Fügt der Debug-Sektion eine Exception-Liste hinzu, sofern noch keine Exception-Liste vorhanden ist. @n
        ## Dieser Liste wird die aktuelle Exception hinzugefügt.
        ## Liegt dem System zum Zeit des Aufrufs keine Exception vor, bleibt die Liste unverändert.
        ## Es werden bis zu 15 unterschiedliche Exceptions angezeigt.
        ## Bei Überlauf verschwindet der älteste Eintrag. @n
        ## Die Darstellung einer Exception: @n
        ##     ZEITSTEMPEL (ANZAHL) @n
        ##     EXCEPTION @n
        ## @note Wird die gleiche Exception mehrfach hinzugefügt, wird sie nur einmal dargestellt und mit einem Zähler versehen.
        ## @param comment @e string @n Optional. Kommentar zur Exception.
        ## @chg20_04 Hinweis zur Exception-Liste ergänzt
        ## @if OLD_CHANGELOG
        ## @chlg04 Parameter comment hinzugefügt
        ## @chlg03 Parameter wurden entfernt. Text angepasst.
        ## @endif
        def add_exception(self, comment=None):
            ## @cond NO_DOC
            try:
                text = None
                try:
                    exc = sys.exc_info()
                    if (exc!=None) and (len(exc)>=3) and (exc[0]!=None):
                        lines = traceback.format_exception(exc[0], exc[1], exc[2])
                        text = ''
                        for line in lines[1:]:
                            text = text + line
                finally:
                    del exc

                if text!=None:
                    # Kann aus unterschiedlichen Threads aufgerufen werden
                    with self.__lock:
                        for item in self.__exceptions:
                            if item[1]==text and item[3]==comment:
                                item[0] = time.time()
                                item[2]+=1
                                return
                        self.__exceptions.append([time.time(),text,1,comment])
                        self.__exceptions.sort()
                        if len(self.__exceptions)>15:
                            del self.__exceptions[0]
            except Exception as e:
                print "add_exception()", e
            ## @endcond


        ## Liefert die bisher gesammelten Debug-Informationen zurück. Der Wert enthält dabei ein weiteres Dictionary mit 3 Feldern.
        ##
        ## @note Wird beim Erzeugen der Debug-Seite vom HS/FS aufgerufen.
        ## @result @e dictionary @n Debug-Informationen
        ## @if OLD_CHANGELOG
        ## @chlg03 Neu
        ## @chlg04 Beschreibung erweitert
        ## @endif
        def get_debug_information(self):
            ## @cond NO_DOC
            result = {}
            result["fields"] = {}
            result["messages"] = []
            result["exceptions"] = []
            with self.__lock:
                for key in self.__fields:
                    result["fields"][key] = self.__fields[key]
                for msg in self.__messages:
                    result["messages"].append([msg[0], msg[1], msg[2]])
                for exc in self.__exceptions:
                    result["exceptions"].append([exc[0], exc[1], exc[2], exc[3]])
            return result
            ## @endcond

        ## @cond NO_DOC
        # Registriert einen Callback für die Erzeugung von Laufzeit-Information auf der Debug-Seite.
        # Wird die Debug-Seite generiert, wird die registrierte Callback-Methode aufgerufen.
        #
        # Die aufgerufenen Callback-Methode hat keine Parameter und muss ein Dictionary zurückgeben.
        #
        # @note Alle registrierten Callbacks werden beim Erzeugen der Debug-Seite aufgerufen.
        # @param callback @e function @n Callback-Methode
        def _internal_register(self, callback):
            idx = None
            try:
                idx = self.__callbacks.index(callback)
            except:
                pass
            if idx==None:
                self.__callbacks.append(callback)

        def _internal_get_callbacks(self):
            return self.__callbacks

        ## @endcond
