#!/usr/bin/python
# -*- coding: UTF-8 -*-


"""
    Code pour la declaration des classes de configuration lue dans les fichiers
    XML avec SAX.
"""


###############################################################################
# Informations de versions
###############################################################################
__project__   = 'Formation Python'
__author__    = 'Simon CHOLLET (s.chollet@llr.in2p3.fr)'
__modifiers__ = ''
__date__      = '11/09/2011'
__version__   = '1.0'


###############################################################################
# Importations
###############################################################################


###############################################################################
# Definitions specifiques
###############################################################################


###############################################################################
# Declaration des classes / fonctions
###############################################################################
class SimpleCfg():
    """
        Classe qui permet de stocker une configuration simple.
        -----------------------------------------------------------------------
        Attributs :
            - m_author : Auteur de la configuration.
            - m_email  : Adresse email de contact.
            - m_institut : Institut.
            - m_date     : Date de creation.
            - m_version  : Version de fichier de configuration.
    """

    # Attributs avec initialisation par defaut
    m_author   = ""
    m_email    = ""
    m_institut = {}
    m_date     = ""
    m_version  = ""


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Initialisation des attributs
        self.m_author   = ""
        self.m_email    = ""
        self.m_institut = {}
        self.m_date     = ""
        self.m_version  = ""


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display  = ">>> SimpleCfg.__repr__() <<<\n"
        display += " * Auteur   : %s\n" % (self.m_author)
        display += " * Email    : %s\n" % (self.m_email)
        if len(self.m_institut):
            display += " * Institut : %s, %s\n" % (self.m_institut['nom'], self.m_institut['address'])
        else:
            display += " * Institut : ???, ???\n"
        display += " * Date     : %s\n" % (self.m_date)
        display += " * Version  : %s\n" % (self.m_version)

        # Retourne ce qu'il faut afficher
        return display


    def Parse(self, pile, data=None):
        """
            Methode qui permet de mettre a jour la configuration a partir d'une
            pile de balises XML.
            -------------------------------------------------------------------
            Arguments :
                - pile : Pile contenant les balises XML a analyser.
                - data : Donnees entre balises, None par defaut.
            -------------------------------------------------------------------
            Retour :
                True si OK, False si erreur.
        """
        # Selon le nom de la balise
        balise = pile[-1]
        if (balise[0] == "author") and data:
            self.m_author = data
        elif (balise[0] == "email") and data:
            self.m_email = data
        elif balise[0] == "institut":
            self.m_institut = {'nom': balise[1]['nom'],
                               'address': balise[1]['address']}
        elif (balise[0] == "date") and data:
            self.m_date = data
        elif (balise[0] == "version") and data:
            self.m_version = data

        # Tout va bien
        return True

class Value():
    """
        Classe qui permet de definir une configuration d'une valeur de mesure.
        -----------------------------------------------------------------------
        Attributs :
            - m_factor : Facteur d'echelle a appliquer.
            - m_min    : Valeur minimale.
            - m_max    : Valeur maximale.
    """

    # Attributs avec initialisation par defaut
    m_factor = 1.0
    m_min    = -1000.0
    m_max    = 1000.0


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Initialisation des attributs
        self.m_factor = 1.0
        self.m_min    = -1000.0
        self.m_max    = 1000.0


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display  = "             - Valeur f=%lf, [%lf..%lf]" % (self.m_factor, self.m_min, self.m_max)

        # Retourne la chaine a afficher
        return display

class Channel():
    """
        Classe qui permet de definir une configuration d'un canal.
        -----------------------------------------------------------------------
        Attributs :
            - m_id          : Identifiant du canal.
            - m_ref         : Reference.
            - m_label       : Description courte.
            - m_ioput       : Le type d'entree / sortie.
            - m_type_value  : Le type de la valeur lue.
            - m_unit        : Unite de mesure.
            - m_description : Description longue.
            - m_value       : Les reglages de la valeur.
    """

    # Attributs avec initialisation par defaut
    m_id          = 0
    m_ref         = ""
    m_label       = ""
    m_ioput       = "I"
    m_type_value  = "float"
    m_unit        = ""
    m_description = ""
    m_value       = None


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Initialisation des attributs
        self.m_id          = 0
        self.m_ref         = ""
        self.m_label       = ""
        self.m_ioput       = "I"
        self.m_type_value  = "float"
        self.m_unit        = ""
        self.m_description = ""
        self.m_value       = None


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display  = "          - Canal %d : %s\n" % (self.m_id, self.m_label)

        # Retourne la chaine a afficher
        return display


class Serial():
    """
        Classe qui permet de definir une configuration de ports de
        communication serie.
        -----------------------------------------------------------------------
        Attributs :
            - m_speed   : Vitesse de communication.
            - m_nb_data : Nombre d'octets pour les donnees.
            - m_nb_stop : Nombre de bits d'arret.
            - m_parity  : Parite.
    """

    # Attributs avec initialisation par defaut
    m_speed    = 0
    m_nb_data  = 0
    m_nb_stop  = 0
    m_parity   = ""


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Initialisation des attributs
        self.m_speed    = 0
        self.m_nb_data  = 0
        self.m_nb_stop  = 0
        self.m_parity   = ""


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display  = "             - Serial : %d, %d, %d, %s\n" % (self.m_speed,
                                                                 self.m_nb_data,
                                                                 self.m_nb_stop,
                                                                 self.m_parity)
        # Retourne la chaine a afficher
        return display



class Com():
    """
        Classe qui permet de definir une configuration de ports de
        communication.
        -----------------------------------------------------------------------
        Attributs :
            - m_type    : Type de communication :
                          'Serial', 'Ethernet', 'USB', '1394', 'Other'
            - m_address : Adresse du port de l'appareil.
            - m_port    : Numero de Port de communication.
            - m_need_cr : Permet de savoir si nous devons envoyer des
                              caracteres de fin de ligne en fin de commande.
            - m_serial  : La configuration particuliere liaison serie.
    """

    # Attributs avec initialisation par defaut
    m_type    = ""
    m_address = ""
    m_port    = ""
    m_need_cr = ""
    m_serial  = None


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Initialisation des attributs
        self.m_type    = ""
        self.m_address = ""
        self.m_port    = ""
        self.m_need_cr = ""
        self.m_serial  = None


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display = "          - Comm. : %s, %s\n" % (self.m_address, self.m_port)

        # Si parametres serie
        if self.m_serial:
            display += self.m_serial.__repr__()

        # Retourne la chaine a afficher
        return display


class Device():
    """
        Classe qui permet de definir une configuration d'un peripherique.
        -----------------------------------------------------------------------
        Attributs :
            - m_id          : Identifiant du peripherique.
            - m_ref         : Reference.
            - m_label       : Description courte.
            - m_trademark   : La marque.
            - m_serial_num  : Le numero de serie.
            - m_com         : La configuration des communications.
            - m_description : Description longue.
            - m_channels    : La liste des canaux.
    """

    # Attributs avec initialisation par defaut
    m_id          = 0
    m_ref         = ""
    m_label       = ""
    m_trademark   = ""
    m_serial_num  = ""
    m_com         = None
    m_description = ""
    m_channels    = []


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Initialisation des attributs
        self.m_id          = 0
        self.m_ref         = ""
        self.m_label       = ""
        self.m_trademark   = ""
        self.m_serial_num  = ""
        self.m_com         = None
        self.m_description = ""
        self.m_channels    = []


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display  = "       - Device %d (%d canaux) :\n" % (self.m_id, len(self.m_channels))

        # Un peu plus de details
        display += "          - Descr. : %s\n" % (self.m_label)
        display += "          - Marque, Ref. : %s, %s\n" % (self.m_trademark, self.m_ref)

        # La communication
        display += self.m_com.__repr__()


        # Pour tous les canaux
        for channel in self.m_channels:
            display += channel.__repr__()

        # Retourne la chaine a afficher
        return display

class Zone():
    """
        Classe qui permet de definir une configuration d'une zone.
        -----------------------------------------------------------------------
        Attributs :
            - m_id          : Identifiant de la zone.
            - m_label       : Description courte.
            - m_description : Description longue.
            - m_devices     : La liste des peripheriques du systeme.
    """

    # Attributs avec initialisation par defaut
    m_id          = 0
    m_label       = ""
    m_description = ""
    m_devices     = []


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Initialisation des attributs
        self.m_id          = 0
        self.m_label       = ""
        self.m_description = ""
        self.m_devices     = []


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display = "    - Zone %d : %s (%d devices)\n" % (self.m_id, self.m_label, len(self.m_devices))

        # Pour tous les peripheriques
        for device in self.m_devices:
            display += device.__repr__()

        # Retourne la chaine a afficher
        return display


class System():
    """
        Classe qui permet de definir une configuration d'un systeme.
        -----------------------------------------------------------------------
        Attributs :
            - m_description : Description longue.
            - m_zones       : La liste des zones du systeme.
    """

    # Attributs avec initialisation par defaut
    m_description = ""
    m_zones       = []


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Initialisation des attributs
        self.m_description = ""
        self.m_zones       = []


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display = "   Systeme (%d zones) :\n" % (len(self.m_zones))

        # Pour toutes les zones
        for zone in self.m_zones:
            display += zone.__repr__()

        # Retourne la chaine a afficher
        return display


class FullCfg(SimpleCfg):
    """
        Classe qui permet de stocker une configuration complete.
        -----------------------------------------------------------------------
        Attributs :
            - m_description : Description de la configuration.
            - m_system      : La configuration du systeme.
            - m_nb_systems  : Compteur de declaration de systeme.
            - m_nb_zones    : Compteur de declaration de zone.
            - m_nb_devices  : Compteur de declaration de device.
            - m_nb_channels : Compteur de declaration de canal.
    """

    # Attributs avec initialisation par defaut
    m_description = ""
    m_system      = None
    m_nb_systems  = 0
    m_nb_zones    = 0
    m_nb_devices  = 0
    m_nb_channels = 0


    def __init__(self):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Appel du parent
        SimpleCfg.__init__(self)

        # Initialisation des attributs
        self.m_description = ""
        self.m_system      = None
        self.m_nb_systems  = 0
        self.m_nb_zones    = 0
        self.m_nb_devices  = 0
        self.m_nb_channels = 0


    def __repr__(self):
        """
            Methode qui permet d'imprimer l'objet instancie.
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Construction de la chaine
        display  = ">>> FullCfg.__repr__() <<<\n"
        display += " * Auteur   : %s\n" % (self.m_author)
        display += " * Email    : %s\n" % (self.m_email)
        display += " * Institut : %s, %s\n" % (self.m_institut['nom'], self.m_institut['address'])
        display += " * Date     : %s\n" % (self.m_date)
        display += " * Version  : %s\n" % (self.m_version)
        display += " * Descrip. : %s\n" % (self.m_description)
        display += "--------------------------------------------------------\n"
        display += self.m_system.__repr__()

        # Retourne ce qu'il faut afficher
        return display


    def Parse(self, pile, data=None):
        """
            Methode qui permet de mettre a jour la configuration a partir d'une
            pile de balises XML.
            -------------------------------------------------------------------
            Arguments :
                - pile : Pile contenant les balises XML a analyser.
                - data : Donnees entre balises, None par defaut.
            -------------------------------------------------------------------
            Retour :
                True si OK, False si erreur.
        """
        # Appel de la classe mere
        SimpleCfg.Parse(self, pile, data)

        # Selon le nom de la balise
        last_balise = pile[-1]
        if (last_balise[0] == "description") and (pile[-2][0] == "config") and data:
            self.m_description = data

        # Configuration du systeme
        elif (last_balise[0] == "system") and not(self.m_system):
            # Creation du nouveau systeme
            self.m_system = System()
            self.m_nb_systems += 1

        # Configuration d'une zone
        elif last_balise[0] == "zone":
            # Creation d'une nouvelle zone
            new_zone = Zone()
            # Ajout a la liste de zone
            self.m_system.m_zones.append(new_zone)
            self.m_nb_zones += 1
        elif (last_balise[0] == "id") and (pile[-2][0] == "zone") and data:
            self.m_system.m_zones[-1].m_id = int(data)
        elif (last_balise[0] == "label") and (pile[-2][0] == "zone") and data:
            self.m_system.m_zones[-1].m_label = data
        elif (last_balise[0] == "description") and (pile[-2][0] == "zone") and data:
            self.m_system.m_zones[-1].m_description = data

        # Configuration d'un device
        elif (last_balise[0] == "device") and (pile[-2][0] == "zone"):
            # Creation d'un nouveau device
            new_device = Device()
            # Ajout a la liste de devices
            self.m_system.m_zones[-1].m_devices.append(new_device)
            self.m_nb_devices += 1
        elif (last_balise[0] == "id") and (pile[-2][0] == "device") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_id = int(data)
        elif (last_balise[0] == "ref") and (pile[-2][0] == "device") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_ref = data
        elif (last_balise[0] == "label") and (pile[-2][0] == "device") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_label = data
        elif (last_balise[0] == "trademark") and (pile[-2][0] == "device") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_trademark = data
        elif (last_balise[0] == "serial_num") and (pile[-2][0] == "device") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_serial_num = data
        elif (last_balise[0] == "description") and (pile[-2][0] == "device") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_description = data

        # Configuration d'un canal
        elif (last_balise[0] == "channel") and (pile[-2][0] == "device"):
            # Creation d'un nouveau canal
            new_channel = Channel()
            # Ajout a la liste de canaux
            self.m_system.m_zones[-1].m_devices[-1].m_channels.append(new_channel)
            self.m_nb_channels += 1
        elif (last_balise[0] == "id") and (pile[-2][0] == "channel") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_id = int(data)
        elif (last_balise[0] == "ref") and (pile[-2][0] == "channel") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_ref = data
        elif (last_balise[0] == "label") and (pile[-2][0] == "channel") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_label = data
        elif (last_balise[0] == "ioput") and (pile[-2][0] == "channel") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_ioput = data
        elif (last_balise[0] == "type_value") and (pile[-2][0] == "channel") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_type_value = data
        elif (last_balise[0] == "unit") and (pile[-2][0] == "channel") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_unit = data
        elif (last_balise[0] == "description") and (pile[-2][0] == "channel") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_description = data

        # Configuration d'une valeur
        elif (last_balise[0] == "value") and (pile[-2][0] == "channel") and \
            not(self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_value):
            # Creation d'une nouvelle valeur
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_value = Value()
        elif (last_balise[0] == "factor") and (pile[-2][0] == "value") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_value.m_factor = float(data)
        elif (last_balise[0] == "min") and (pile[-2][0] == "value") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_value.m_min = float(data)
        elif (last_balise[0] == "max") and (pile[-2][0] == "value") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_channels[-1].m_value.m_max = float(data)

        # Configuration d'une communication
        elif (last_balise[0] == "comm") and (pile[-2][0] == "device") and \
            not(self.m_system.m_zones[-1].m_devices[-1].m_com):
            # Creation d'un nouveau moyen de communication
            self.m_system.m_zones[-1].m_devices[-1].m_com = Com()
            # Mise a jour du type
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_type = last_balise[1]['type']
        elif (last_balise[0] == "address") and (pile[-2][0] == "comm") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_address = data
        elif (last_balise[0] == "port") and (pile[-2][0] == "comm") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_port = data
        elif (last_balise[0] == "need_cr") and (pile[-2][0] == "comm") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_need_cr = data

        # Configuration d'une communication RS
        elif (last_balise[0] == "serial") and (pile[-2][0] == "comm") and \
            not(self.m_system.m_zones[-1].m_devices[-1].m_com.m_serial):
            # Creation d'une configuration RS
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_serial = Serial()
        elif (last_balise[0] == "speed") and (pile[-2][0] == "serial") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_serial.m_speed = int(data)
        elif (last_balise[0] == "nb_data") and (pile[-2][0] == "serial") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_serial.m_nb_data = int(data)
        elif (last_balise[0] == "nb_stop") and (pile[-2][0] == "serial") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_serial.m_nb_stop = int(data)
        elif (last_balise[0] == "parity") and (pile[-2][0] == "serial") and data:
            self.m_system.m_zones[-1].m_devices[-1].m_com.m_serial.m_parity = data

        # Tout va bien
        return True
