#!/usr/bin/env python #-*- coding: iso8859-15 -*- ########################################################## # FILE_SIGNALS v1.0 # ########################################################## # Autor: Juan Miguel Taboada Godoy # # Fecha: Szczecin, 02 de Agosto de 2006 # # Descripción: Cargador de datos de ficheros SIGNALS # # Versión: 2006121300 # # # # Codigo fuente bajo licencia GNU/GPL # # Centrologic (Computational Logistic Center) # # http://www.centrologic.com - info@centrologic.com # ########################################################## # Librerías que voy a usar {{{1 import os import time import datetime import math import gzip import fcntl from INTERFACES import READABLE,REGISTRADOR,LockError from HERENCIA import BASE from MATH_GRA import digitos from DATE import dateepoch,epochdate #from DATE import * # }}}1 # Funcion general {{{1 # Errores conocidos por la clase {{{2 def error_conocido(error,return_list=False): # Construyo la lista de errores manejables lista=[] lista.append("ERROR_NET") # Devuelvo el resultado del analisis if (return_list): # Devuelvo la lista de errors conocidos return lista else: # Devuelvo si el error es conocido o no return (error in lista) # }}}2 # Transforma una fecha EPOCH a una cadena de texto sin espacios "dd/mm/aaaa_hh:mm:ss" {{{2 def fecha_str(timestamp): fechahora=datetime.datetime.fromtimestamp(float(timestamp)) fechatuple=fechahora.timetuple() fecha="%s/%s/%s" % (digitos("%s" % fechatuple[2],2),digitos("%s" % fechatuple[1],2),fechatuple[0]) hora="%s:%s:%s" % (digitos("%s" % fechatuple[3],2),digitos("%s" % fechatuple[4],2),digitos("%s" % fechatuple[5],2)) return "%s_%s" % (fecha,hora) # }}}2 # }}}1 # ### FILE_SIGNALS ### ################################ ## \internal ## FILE_SIGNALS: Libreria para manejo de ficheros con formato SIGNALS ## \version 13/12/2006 0958 Szczecin class FILE_SIGNALS(BASE,READABLE,REGISTRADOR): # Constructor {{{1 def __init__(self,formato_fichero=None,fichero=None,frecuencia=None,compress=False): # Cargo las acciones del padre BASE.__init__(self,None,"FILE_SIGNALS") # Guarda el formato de fichero self.__formato_fichero=formato_fichero # Inicializo el contador de elementos self.__elemento_actual=0 # Creo un diccionario vacío self.__lista=[] # Compresion GZIP self.__compress=compress # Retroalimentador self.__feedback={} # Fichero de salida self.__fichero_salida=fichero # Frecuencia interna self.__frecuencia=frecuencia # Caracter de salida para valores nulos self.__nulo='x' # Inicializa a None el mantener bloqueo self.__keeplock=None # Texto de cabecera self.__cabecera="Fecha_Normal Fecha_Epoch Funcion ID Valor (Media) (Maximo) (Minimo) (Desviacion_Tipica)" # Registrar cabecera de no-error errores_conocidos=error_conocido(None,True) self.__cabecera_no_error={} for dato in errores_conocidos: self.__cabecera_no_error[dato]=True # }}}1 # Destructor {{{1 ## Destructor ## \param - self def __del__(self): # Close the file if is locked with keeplock self.close() # }}}1 # Devuelve el formato del fichero {{{1 ## Devuelve el formato del fichero ## \param self - def formato(self): return self.__formato_fichero # }}}1 # Devuelve si el fichero esta comprimido o o no {{{1 ## Devuelve si el fichero esta comprimido o o no ## \param self - def compress(self): return self.__compress # }}}1 # Frecuencia de compresion interna {{{1 ## Frecuencia de compresion interna ## \param self - ## \param value Valor compresión interna (opcional) def frecuencia(self,value=None): if (value==None): return self.__frecuencia else: self.__frecuencia=value # }}}1 # Devuelve la relacion de funcion con tipo {{{1 # \internal # Devuelve la relacion de un numero de funcion con su tipo # \param self - # \param funcion ID de la funcion # \param tipo Tipo de la funcion # \return Devuelve una tupla que contiene: (id,tipo,es_digital?,evento?) def __function_relation(self,funcion=None,tipo=None,grupo=None): if ((funcion=="INTERNAL") or (tipo=="INTERNAL")): # Eventos internos return ("INTERNAL","INTERNAL",True,True) elif (((funcion==1) or (tipo=="data")) and ((grupo==None) or (grupo=="web"))): # Registro de datos return (1,"data",True,False) elif (((funcion==1) or (tipo=="ed")) and ((grupo==None) or (grupo=="momentum"))): # Entrada digital return (1,"ed",True,True) elif (((funcion==2) or (tipo=="sd")) and ((grupo==None) or (grupo=="momentum"))): # Salida digital return (2,"sd",True,True) elif (((funcion==3) or (tipo=="ea")) and ((grupo==None) or (grupo=="momentum"))): # Entrada analógica return (3,"ea",False,False) elif (((funcion==4) or (tipo=="sa")) and ((grupo==None) or (grupo=="momentum"))): # Salida analógica return (4,"sa",False,False) elif (((funcion==1) or (tipo=="rd")) and ((grupo==None) or (grupo=="advantech"))): # Read digital return (1,"rd",True,True) elif (((funcion==2) or (tipo=="wd")) and ((grupo==None) or (grupo=="advantech"))): # Write digital return (2,"wd",True,True) elif (((funcion==3) or (tipo=="ra")) and ((grupo==None) or (grupo=="advantech"))): # Read analogic return (3,"ra",False,False) elif (((funcion==4) or (tipo=="wa")) and ((grupo==None) or (grupo=="advantech"))): # Write analogic return (4,"wa",False,False) elif (((funcion==1) or (tipo=="bi")) and ((grupo==None) or (grupo=="netstatistics"))): # Banda de entrada (band input) return (1,"bi",False,False) elif (((funcion==2) or (tipo=="bo")) and ((grupo==None) or (grupo=="netstatistics"))): # Banda de salida (band output) return (2,"bo",False,False) elif (((funcion==3) or (tipo=="pi")) and ((grupo==None) or (grupo=="netstatistics"))): # Paquetes de entrada (packet input) return (3,"pi",False,False) elif (((funcion==4) or (tipo=="po")) and ((grupo==None) or (grupo=="netstatistics"))): # Paquetes de salida (packet output) return (4,"po",False,False) elif (((funcion==5) or (tipo=="hp")) and ((grupo==None) or (grupo=="netstatistics"))): # Hay ping return (5,"hp",True,True) elif (((funcion==6) or (tipo=="pe")) and ((grupo==None) or (grupo=="netstatistics"))): # Ping Enviados return (6,"pe",False,False) elif (((funcion==7) or (tipo=="pr")) and ((grupo==None) or (grupo=="netstatistics"))): # Ping Recibidos return (7,"pr",False,False) elif (((funcion==8) or (tipo=="pp")) and ((grupo==None) or (grupo=="netstatistics"))): # Ping Perdidos (%) return (8,"pp",False,False) elif (((funcion==9) or (tipo=="pm")) and ((grupo==None) or (grupo=="netstatistics"))): # Ping Minimo return (9,"pm",False,False) elif (((funcion==10) or (tipo=="pa")) and ((grupo==None) or (grupo=="netstatistics"))): # Ping Medio (Average) return (10,"pa",False,False) elif (((funcion==11) or (tipo=="px")) and ((grupo==None) or (grupo=="netstatistics"))): # Ping Maximo return (11,"px",False,False) elif (((funcion==12) or (tipo=="pv")) and ((grupo==None) or (grupo=="netstatistics"))): # Ping Dispositivo (mdev) return (12,"pv",False,False) else: if (funcion==None): if (tipo==None): raise IOError,"No ha indicado en la busqueda funcion o tipo" else: raise IOError,"El tipo '%s' y grupo '%s' son desconocidos" % (tipo,grupo) else: raise IOError,"La funcion '%s', tipo '%s' y grupo '%s' son desconocidos" % (funcion,tipo,grupo) # }}}1 # Devuelve el nombre de fichero {{{1 ## Devuelve el nombre de fichero ## \param self - def fichero(self): return self.__fichero_salida # }}}1 # Elimina la informacion de retroalimentacion {{{1 ## \internal ## Elimina la informacion de retroalimentacion de la clase almacenada hasta el momento ## \param self - def resetreg(self): self.__feedback={} # }}}1 # Anade un elemento a la lista {{{1 ## \internal ## Inserta un elemento en la lista de datos ## \param self - ## \param id ID de la senal ## \param date Fecha y hora ## \param value Valor contenido ## \return True si lo ha insertado, False si no lo ha insertado def __add(self,id,date,value): # Comprueba la condicción de carga if (value!=""): # Añade el elemento a la lista self.__lista.append((id,date,value)) # Devuelve que si lo añadió return True else: # Devuelve que no lo añadió return False # }}}1 # Carga el fichero de datos {{{1 ## \internal ## Carga el fichero de datos ordenadamente en la memoria ## \param self - ## \param fichero Fichero a leer ## \param flock nolock:No bloquear, wait:esperar a que se libere el fichero, lock:bloquear o fallar (Por defecto: nolock) ## \param keeplock Indica si mantener el bloqueo despues del load (Por defecto: False) def load(self,fichero,flock="nolock",keeplock=False): # Revisa si la extension del fichero es gz if (fichero.split(".")[-1]=="gz"): # En tal caso abro el fichero con el modulo gzip file=gzip.open(fichero,"r") # Bloqueo el acceso al fichero si necesario if (flock=="wait"): fcntl.flock(file.fileno(), fcntl.LOCK_EX) elif (flock=="lock"): try: fcntl.flock(file.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB) except IOError,error: raise LockError,error # Recuerdo que está comprimido self.__compress=True else: # Si es un fichero normal lo abro normalmente file=open(fichero,"r") # Bloqueo el acceso al fichero si necesario if (flock=="wait"): fcntl.flock(file.fileno(), fcntl.LOCK_EX) elif (flock=="lock"): try: fcntl.flock(file.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB) except IOError,error: raise LockError,error # Recuerdo que no está comprimido self.__compress=False # Proceso el fichero {{{2 self.debug("Procesando el fichero") # Cargo el fichero contenido=file.readlines() # Extraigo el tipo tipo=contenido[0] tipo=tipo.split("-") if ((len(tipo)==3) or (len(tipo)==4)): # Consigue la frecuencia del fichero o compresion if (len(tipo)==4): compresion=tipo[3] compresion=compresion.split(":")[1] self.__frecuencia=int(compresion) self.debug(" -> Fichero comprimido con frecuencia: %s" % (self.__frecuencia)) else: self.__frecuencia=None # Revisa el tipo tipo=tipo[2] tipo=tipo.split(":") if (len(tipo)==2): if (tipo[1][0]==' '): ini=1 else: ini=0 if ((tipo[1][-1]=='\n') or (tipo[1][-1]==' ')): tipotelemando=tipo[1][ini:-1] else: tipotelemando=tipo[1][ini:] else: tipotelemando="momentum" else: tipotelemando="momentum" # Recuerdo el formato del fichero self.__formato_fichero=tipotelemando # Para cada linea for linea in contenido[1:]: linea=linea.split('\n')[0] linea=linea.split(" ") if ((len(linea)==5) or (len(linea)==9)): # Valores timestamp=float(linea[1]) # Recojo la función if (linea[2]=="INTERNAL"): funcion=linea[2] else: funcion=int(linea[2]) # Obtiene los datos de la funcion funcrelat=self.__function_relation(funcion,None,tipotelemando) # (funcion,tipo,digital,evento)=funcrelat funcion=funcrelat[0] tipo=funcrelat[1] digital=funcrelat[2] # Extrae mas valores posicion=linea[3] real=linea[4] # Interpreto si el real es nulo if (real==self.__nulo): real=None # Si es digital if (not digital): media=linea[5] maximo=linea[6] minimo=linea[7] varianza=linea[8] # Interpreto los valores nulos if (media==self.__nulo): media=None if (maximo==self.__nulo): maximo=None if (minimo==self.__nulo): minimo=None if (varianza==self.__nulo): varianza=None # Transformaciones de la fecha fechahora=datetime.datetime.fromtimestamp(float(timestamp)) fechatuple=fechahora.timetuple() fecha="%s-%s-%s" % (fechatuple[0],digitos("%s" % fechatuple[1],2),digitos("%s" % fechatuple[2],2)) hora="%s:%s:%s" % (digitos("%s" % fechatuple[3],2),digitos("%s" % fechatuple[4],2),digitos("%s" % fechatuple[5],2)) # Cargo el ultimo dato registrado self.__add("%s:%s" % (tipo,posicion),(fecha,hora),real) # Valor en el instante # Si es digital almacena sus valores if (not digital): self.__add("%smed:%s" % (tipo,posicion),(fecha,hora),media) # Valor medio self.__add("%smax:%s" % (tipo,posicion),(fecha,hora),maximo) # Valor máximo self.__add("%smin:%s" % (tipo,posicion),(fecha,hora),minimo) # Valor mínimo self.__add("%svar:%s" % (tipo,posicion),(fecha,hora),varianza) # Valor de la varianza else: self.debug("Cadena incorrecta en el fichero '%s', se rechaza: %s" % (fichero,linea)) # }}}3 # }}}2 # Inserto los elementos restantes si el fichero estaba comprimido {{{2 if (self.__frecuencia!=None): # Recojo la frecuencia frecuencia=self.__frecuencia # Ordeno la lista temporal de datos lista=[] for elemento in self.__lista: lista.append(elemento) lista.sort() # Para cada elemento proceso el resultado old=None unico=False for elemento in lista: # Consigue el ID del elemento if ((old==None) or (old[0]!=elemento[0])): # Comprueba si era un elemento unico if (unico): # Cargo la nueva fecha oldepoch=dateepoch(old[1][0],old[1][1]) (fecha,hora)=epochdate(oldepoch+frecuencia) # Creo un nuevo elemento newelemento=(old[0],(fecha,hora),old[2]) # Inserto el nuevo elemento en la lista self.__lista.append(newelemento) # Unico unico=True else: # Indica que ya no es un elemento unico unico=False # Comprueba el timestamp actual oldepoch=dateepoch(old[1][0],old[1][1]) newepoch=dateepoch(elemento[1][0],elemento[1][1]) while (oldepoch+frecuenciafmax) or (fmax==None)): maximo=valor else: maximo=fmax # Calculo el minimo if ((valorNone o None->Valor if ((evento and ((digital and (valor!=fdato)) or ((not digital) and ((1.001*valor>fdato) and (0.999*valorfdato) and (0.999*valor