#!/usr/bin/env python #-*- coding: iso8859-15 -*- ########################################################## # CARGADOR SQL v1.0 # ########################################################## # Autor: Juan Miguel Taboada Godoy # # Fecha: Szczecin, 28 de Julio de 2006 # # Descripción: Cargador de datos a SQL # # Versión: 2006071900 # # Interpreta los formatos de fichero: # # - PESYR # # - MODBUS::SIGNALS # # # # 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 MySQLdb from lib.FILE_PESYR import FILE_PESYR from lib.FILE_SIGNALS import FILE_SIGNALS from lib.MATH_GRA import normalizar_float, digitos from lib.DATE import datestrcmp, epochdate, dateepoch from lib.HERENCIA import BASE,DEFAULTCONFIG from lib.INTERFACES import LockError # }}}1 # Funciones generales {{{1 # Funciones para convertir Hexadecimales a Binarios {{{2 # Función para convertir un caracter Hexadecimal a binario {{{3 def hex1tobin(char): if (char=='0'): return "0000" if (char=='1'): return "0001" if (char=='2'): return "0010" if (char=='3'): return "0011" if (char=='4'): return "0100" if (char=='5'): return "0101" if (char=='6'): return "0110" if (char=='7'): return "0111" if (char=='8'): return "1000" if (char=='9'): return "1001" if ((char=='A') or (char=='a')): return "1010" if ((char=='B') or (char=='b')): return "1011" if ((char=='C') or (char=='c')): return "1100" if ((char=='D') or (char=='d')): return "1101" if ((char=='E') or (char=='e')): return "1110" if ((char=='F') or (char=='f')): return "1111" return "xxxx" # }}}3 # Función para convertir una cadena Hexadecimal a una cadena Binaria{{{3 def hextobin(hexadecimal): # Comenzamos el proceso binary="" for hexachar in hexadecimal: binary="%s%s" % (binary,hex1tobin(hexachar)) return binary # }}}3 # }}}2 # Funciones para convertir Decimales a Binarios {{{2 # Función para convertir un número decimal a binario {{{3 def dectobin(numero,longitud): # Creo la cadena vacía binario="" # Ajusto la memoria del algoritmo memoria=1 # Si el número es 0 if (numero==0): # El binario es 0 binario="0" # Hay un 0 añadido a la cadena contador=1 else: # Inicializo el contador contador=0 # Mientras el número no sea 0 while (numero!=0): # Actualizo la memoria memoria=numero % 2 # Incremento el binario binario="%s%s" % (memoria,binario) # Incremento el contador contador=contador+1 # Actualizo el número numero=numero/2 # Reviso posibles errores if (contador>longitud): raise Exception,"SQL ERROR: DECTOBIN fuera de rango" # Ajusto el binario a la longitud solicitada while (contador(contador+frecuencia)): contador=contador+frecuencia (fecha,hora)=epochdate(contador) nueva_lista.append((elemento[0],fecha,hora,None,elemento[4])) nueva_lista.append(elemento) contador=dateepoch(elemento[1],elemento[2]) return nueva_lista # }}}2 # Comprime los datos entregados {{{2 # Formato: (id,fecha,hora,valor,tipo) # Función que indica si un valor debe ser comprimido o no (con respecto a otro) {{{3 def __compress(feedback,dato): # Función de compresión por igualdades básicas (iguales,distintos_None) if (feedback==dato): return True elif (((feedback!=None) and (dato==None)) or ((feedback==None) and (dato!=None))): return False # Función de compresión por similitud (aquí podemos asegurar que feedback y dato son números) dato=float(dato) feedback=float(feedback) # Realizo la comprobacion try: #if ((1.00001*dato>=feedback) and (0.99999*dato<=feedback)): if (dato==feedback): return True except Exception: return False # En otro caso, no lo comprimas return False # }}}3 def compress(lista): # Inicializo los datos nueva_lista=[] analizando=None anterior=None anterior_compressed=False primero=True done=False # Para toda la lista for dato in lista: # Si es el primer elemento lo cargo directamente en la nueva lista if (primero): analizando=normalizar_float(dato[3]) # Cargo el primer elemento en la nueva lista nueva_lista.append((dato[0],dato[1],dato[2],analizando,dato[4])) # Ya no hay mas primeros primero=False # Anoto que este elemento no ha sido comprimido anterior_comprimido=False else: analizando_new=normalizar_float(dato[3]) # Compruebo si debo comprimir o no el dato actual y si no son elementos con la misma fecha if (not __compress(analizando,analizando_new)) and (datestrcmp(anterior[1],anterior[2],lista[0][1],lista[0][2])!=0): # Si el dato anterior fue comprimido, lo inserto para cerrar la secuencia comprimida if (anterior_compressed): nueva_lista.append((anterior[0],anterior[1],anterior[2],normalizar_float(anterior[3]),anterior[4])) # Inserto el dato que estamos procesando nueva_lista.append((dato[0],dato[1],dato[2],analizando_new,dato[4])) # Anoto que el dato no ha sido comprimido (asi no se inserta de nuevo en la secuencia si esta cambia) anterior_compressed=False else: # Anoto que el dato ha sido comprimido anterior_compressed=True analizando=analizando_new # Recuerdo el elemento analizado anterior=dato # Reviso la insercción del último elemento si no ha sido insertado aún nueva_lista.append(dato) return nueva_lista # }}}2 # }}}1 # ### CONFIG ### ###################################### ## Exportador a la base de datos: Configuracion por defecto class CONFIG(DEFAULTCONFIG): # Constructor {{{1 def __init__(self): # Cargo las acciones del padre DEFAULTCONFIG.__init__(self,"CARGADOR") # }}}1 # Config Cargador {{{1 # Directorio con los registros {{{2 ## Directorio con los registros ## \param self - ## \param arg Directorio donde encontrar los registros a procesar def dirreg(self,arg=None): if (arg!=None): self._dirreg=arg return self._dirreg # }}}2 # Host {{{2 ## Servidor SQL ## \param self - ## \param arg Direccion o nombre del servidor SQL def sql_host(self,arg=None): if (arg!=None): self._sql_host=arg return self._sql_host # }}}2 # Port {{{2 ## Puerto del servidor SQL ## \param self - ## \param arg Puerto del servidor SQL def sql_port(self,arg=None): if (arg!=None): self._sql_port=arg return self._sql_port # }}}2 # Sockect {{{2 ## Sockect del servidor SQL ## \param self - ## \param arg Socket del servidor SQL def sql_socket(self,arg=None): if (arg!=None): self._sql_socket=arg return self._sql_socket # }}}2 # User {{{2 ## Usuario de acceso al servidor SQL ## \param self - ## \param arg Usuario para acceder al servidor SQL def sql_user(self,arg=None): if (arg!=None): self._sql_user=arg return self._sql_user # }}}2 # Password {{{2 ## Clave de acceso al servidor SQL ## \param self - ## \param arg Clave del usuario para accder al servidor SQL def sql_passwd(self,arg=None): if (arg!=None): self._sql_passwd=arg return self._sql_passwd # }}}2 # Base de datos {{{2 ## Nombre de la base de datos ## \param self - ## \param arg Nombre de la base de datos def sql_db(self,arg=None): if (arg!=None): self._sql_db=arg return self._sql_db # }}}2 # Tabla de datos {{{2 ## Tabla de datos ## \param self - ## \param arg Tabla con los datos def sql_table(self,arg=None): if (arg!=None): self._sql_table=arg return self._sql_table # }}}2 # Frecuencia de muestreo {{{2 ## Frecuencia de muestreo ## \param self - ## \param arg Frecuencia de muestro de los datos (en segundos) def frecuencia(self,arg=None): if (arg!=None): self._frecuencia=arg return self._frecuencia # }}}2 # }}}1 # Checker {{{1 ## Comprueba que el conjunto minimo de datos requeridos por la clase han sido declarados por el usuario: \n ## - dirreg() ## - sql_user() ## - sql_passwd() ## - sql_db() ## - sql_table() ## - sql_host() ## - sql_port() ## - sql_socket() ## - frecuencia() ## \param self - def check(self): self._check("dirreg") self._check("sql_user") self._check("sql_passwd") self._check("sql_db") self._check("sql_table") self._check("sql_host") self._check("sql_port") self._check("sql_socket") self._check("frecuencia") self.check_default() # }}}1 # ### TELEMANDO ### ################################### ## Exportador a la base de datos: Cargador class CARGADOR(BASE): # Constructor {{{1 def __init__(self): # Cargo las acciones del padre BASE.__init__(self,id,"CARGADOR") # Inicializo el estado interno del Telemando self.__incongruencias=[] # Tabla de señales self.__tablasenales=[] # }}}1 # Añade señales al cargador {{{1 ## Carga la tabla de senales en el cargador para ser usada durante la lectura de datos para interpretar los datos leidos y hacerlos corresponder en la Base de datos ## \param self - ## \param tablasenales Tabla de senales def senales(self,tablasenales=None): self.__tablasenales=tablasenales # }}}1 # Carga las Bases de Datos {{{1 ## Carga los datos a la Base de Datos ## \param self - def load(self): # Cacheo algunas funciones importantes {{{2 buscador=self.__buscador time_cache=time.time debug_cache=self.debug # }}}2 # Abro la conexión SQL {{{2 self.debug("Abriendo conexion a la base de datos") try: # Control sobre autodetección del socket if (self.valor("sql_socket")=="auto"): # MySQL socket por defecto self.debug(" Conectando con socket autoconfigurado (BDconnection)") BDconnection = MySQLdb.connect(host=self.valor("sql_host"),port=self.valor("sql_port"), user=self.valor("sql_user"), passwd=self.valor("sql_passwd"), db=self.valor("sql_db")) self.__BDconnection=BDconnection else: # MySQL socket definido en la configuración self.debug(" Conectando con socket manual (BDconnection)") BDconnection = MySQLdb.connect(host=self.valor("sql_host"),port=self.valor("sql_port"), user=self.valor("sql_user"), passwd=self.valor("sql_passwd"), db=self.valor("sql_db"), unix_socket=self.valor("sql_socket")) # Activo el cursor self.debug(" Cargando cursor (BD)") BD = BDconnection.cursor() self.__BD=BD except MySQLdb.OperationalError, message: raise IOError,"SQL ERROR: Error número %d al conectar a la Base de Datos:\n%s" % (message[0],message[1]) # }}}2 # Obtiene la lista de ficheros a procesar {{{2 self.debug("Obtengo la lista de fichero a procesar") lista=os.listdir(self.valor("dirreg")) lista.sort() totalficheros_procesar=len(lista) totalficheros_procesando=0 # }}}2 # Para cada fichero {{{2 listaborrar=[] for nombre in lista: # Abro un control de excepciones try: # Obtengo el telemando telemando=nombre.split("_")[0] # Obtengo las senales del Telemando senales_del_telemando=self.__tablasenales.search_tele(telemando) # Reviso el tipo de fichero {{{3 extensiones=nombre.split(".") # Compruebo si es un fichero comprimido if (extensiones[-1]=="gz"): # Extraigo el tipo de fichero contando que existe .gz tipo=nombre.split(".")[-3] else: # Extraigo el tipo de fichero normalmente tipo=nombre.split(".")[-2] if (tipo=="pesyr"): totalficheros_procesando=totalficheros_procesando+1 self.debug("Procesando fichero...%s (PESYR) - %s/%s (%s%%)" % (nombre,totalficheros_procesando,totalficheros_procesar,(totalficheros_procesando*100)/totalficheros_procesar)) # Creo el tipo adecuado file=FILE_PESYR() file.set_debug(self.set_debug()) fichero="%s%s" % (self.valor("dirreg"),nombre) if (not os.path.exists(fichero)): raise NoFile,"Fichero %s no existe" % (fichero) file.load(fichero,"lock",True) self.debug("Cargando a la Base de Datos...") dato=file.next() known_file=True tipotelemando="pesyr" elif (tipo=="signal"): totalficheros_procesando=totalficheros_procesando+1 self.debug("Procesando fichero...%s (SIGNALS) - %s/%s (%s%%)" % (nombre,totalficheros_procesando,totalficheros_procesar,(totalficheros_procesando*100)/totalficheros_procesar)) # Creo el tipo adecuado file=FILE_SIGNALS() file.set_debug(self.set_debug()) fichero="%s%s" % (self.valor("dirreg"),nombre) if (not os.path.exists(fichero)): raise NoFile,"Fichero %s no existe" % (fichero) file.load(fichero,"lock",True) self.debug("Cargando a la Base de Datos...") dato=file.next() known_file=True tipotelemando=file.formato() else: self.debug("Procesando fichero...%s (DESCONOCIDO) - No se procesa - %s/%s (%s%%)" % (nombre,totalficheros_procesando,totalficheros_procesar,(totalficheros_procesando*100)/totalficheros_procesar)) dato=None known_file=False tipotelemando=None # }}}3 # Cargo el fichero a la lista de carga de la Base de Datos {{{3 total_datos_seleccionar=file.lenght() if (known_file): self.debug("Seleccionando datos a cargar de un total de: %s elementos" % (total_datos_seleccionar)) #compresor=None lista=[] percent=0 percent_control=-1 percent_timer=time.time() is_debuging=self.set_debug() while dato: # Show percent every 60 seconds if (is_debuging): percent_control+=1 if ((time_cache()-percent_timer)>=60): # Remember percent_timer=time_cache() # Calculate percent percent=int(percent_control*100/total_datos_seleccionar) # Show it debug_cache(" -> %s%%" % (percent)) # Si el dato no viene vacío if dato[2]!='NULL': # Proceso el dato recibido buscando por la señal del telemando indicado listabusqueda=buscador(telemando,tipotelemando,senales_del_telemando,dato) for busqueda in listabusqueda: # Saca los datos de busqueda {{{4 id=busqueda[0] lfecha=busqueda[1] fecha=lfecha[0] hora=lfecha[1] tipo=busqueda[2] valor=busqueda[3] # }}}4 # Añado este valor a la lista a procesar en la Base de Datos {{{4 lista.append((id,fecha,hora,valor,tipo)) # }}}4 # Obtengo otro elemento dato=file.next() # }}}3 # Comprimo la lista de datos {{{3 compresion_total=len(lista) self.debug("Comprimiendo %s elementos" % (compresion_total)) self.debug(" -> Ordenando lista") # Ordeno el resultado lista.sort() # Realiza el particionado según los tipos de datos (sublistas) temp=0 nueva_lista=[] query_borrar=[] total_largo_lista=len(lista) percent=0 percent_timer=time.time() self.debug(" -> Particionado y compresion") while (temp=60)): # Remember percent_timer=time.time() # Calculate percent percent=int(temp*100/total_largo_lista) # Show it self.debug(" -> %s%%" % (percent)) # Busco el último primero=temp ultimo=temp while ((temp='%s') OR fecha>'%s') " % (fecha_ini,hora_ini,fecha_ini)) borrar="%s%s" % (borrar,"AND ((fecha='%s' AND hora<='%s') OR fecha<'%s') " % (fecha_fin,hora_fin,fecha_fin)) query_borrar.append(borrar) # Rango a procesar sublista_temp=lista[primero:ultimo+1] # Elimino los valores repetidos (por eventos ocurridos en intervalos menores al segundo) sublista=[] indice=0 total=len(sublista_temp) for elemento in sublista_temp: # Compruebo si el siguiente elemento tiene la misma fecha y hora (me quedo con el elemento mas reciente) if ((indice+1==total) or (elemento[1]!=sublista_temp[indice+1][1]) or (elemento[2]!=sublista_temp[indice+1][2])): # Inserto el elemento sublista.append(elemento) # Incremento el indice indice+=1 # Relleno los huecos vacíos de los datos obtenidos del fichero rellenada=relleno(sublista,frecuencia) # Comprimo la lista rellenada comprimida_temp=compress(rellenada) if (comparar_similares(comprimida_temp[-1],comprimida_temp[-2])): comprimida_temp=comprimida_temp[0:-1] # Localizo el elemento basedatos-anterior y el elemento Basedatos-posterior (anterior,posterior)=self._anterior_posterior(id_senal,fecha_ini,hora_ini,fecha_fin,hora_fin) # Compresion del elemento inicial (si iguales primero-secuencia y basedatos-anterior, se borra primero-secuencia) if ((anterior!=None) and (comparar_similares(comprimida_temp[0],anterior))): comprimida=comprimida_temp[1:] else: comprimida=comprimida_temp # Compresion del elemento final (si iguales ultimo-secuencia y basedatos-posterior, se borra basedatos-posterior) if (posterior!=None): if ((comprimida!=[]) and (comparar_similares(posterior,comprimida[-1]))): borrar="" borrar="%s%s" % (borrar,"DELETE ") borrar="%s%s" % (borrar,"FROM datos WHERE id=%s " % (id_senal)) borrar="%s%s" % (borrar,"AND fecha='%s' AND hora='%s' " % (posterior[1],posterior[2])) query_borrar.append(borrar) else: # Si el ultimo elemento de la cadena comprimida no es None y no hay mas datos en la base de datos, limito el resultado if ((comprimida==[]) or (comprimida[-1][3]!=None)): # Transformo la fecha a EPOCH epoch=dateepoch(fecha_fin,hora_fin) # Decremento en un segundo timestamp=epoch+frecuencia # Transformo a fecha/hora normal (fecha,hora)=epochdate(timestamp) # Prepara la fecha (a,m,d)=fecha.split("-") fecha="%s-%s-%s" % (a,digitos(m,2),digitos(d,2)) # Prepara la hora (h,m,s)=hora.split(":") hora="%s:%s:%s" % (digitos(h,2),digitos(m,2),digitos(s,2)) # Genero elemento de corte con frecuencia elemento=(dato_senal_id,fecha,hora,None,dato_senal_tipo) # Inserto el elemento en la cadena comprimida comprimida.append(elemento) # Añado los nuevos elementos a la secuencia resultante for elemento in comprimida: # Inserto el elemento nueva_lista.append(elemento) # Tamaño de la lista resultante de la compresión compresion_comprimidos=len(nueva_lista) # Controla que hayamos procesado datos if (compresion_total==0): # Si no hay señales para cargar datos a la BD la lista sera vacía self.debug("Ratio de compresion: No existe") else: # Calculo el ratio de compresión self.debug("Ratio de compresion: %.2f%%" % (float(compresion_comprimidos)*100.0/float(compresion_total))) # }}}3 # Envío el query a la base de datos para borrar los registros extraidos anteriormente {{{3 self.debug("Borrando registros antiguos de la Base de Datos") #self.debug("Enviando QUERY: %s" % (query_borrar)) if ((self.__BD==None) or (self.__BDconnection==None)): raise IOError,"DB_MYSQL: Se intentó un QUERY pero el sistema no esta correctamente conectado a la Base de Datos (%s)" % (query_borrar) percent=0 percent_control=0 percent_timer=time.time() percent_total=len(query_borrar) for query in query_borrar: # Show percent every 60 seconds if (self.set_debug() and ((time.time()-percent_timer)>=60)): # Remember percent_timer=time.time() # Calculate percent percent=int(percent_control*100/percent_total) # Show it self.debug(" -> %s%%" % (percent)) # Launch query to database try: self.__BD.execute(query) self.__DBresult=self.__BD.rowcount except MySQLdb.OperationalError, message: # No hubo resultados de la busqueda self.debug("No se han encontrado resultados") self.__DBresult=None except Exception, message: # Error en la operación raise IOError,"DB_MYSQL OPERATIONAL ERROR: Error %d: %s\n(QUERY: %s) [PELIGRO: Es posible que se haya producido una perdida de datos]" % (message[0],message[1],query) # Next one percent_control+=1 # }}}3 # Cargo los datos a la base de datos {{{3 self.debug("Cargando registros nuevos en la Base de Datos: %s elementos" % (compresion_comprimidos)) percent=0 percent_control=0 percent_timer=time.time() percent_total=len(nueva_lista) for elemento in nueva_lista: # Show percent every 60 seconds if (self.set_debug() and ((time.time()-percent_timer)>=60)): # Remember percent_timer=time.time() # Calculate percent percent=int(percent_control*100/percent_total) # Show it self.debug(" -> %s%%" % (percent)) # Get the element (id,fecha,hora,valor,tipo)=elemento # Control de None {{{4 if (valor==None): valor="NULL" # }}}4 # Preparo los valores digitales/analogicos {{{4 if (tipo=='d'): valora="NULL" valord=valor elif (tipo=='a'): valora=valor valord="NULL" else: raise IOError,"SQL ERROR: Error en el tipo de dato entregado en la senal (solo 'd':digital o 'a':analogico) [PELIGRO: Es posible que se haya producido una pedida de datos]" # }}}4 # Genero el query {{{4 sqlquery="INSERT INTO %s (id,fecha,hora,valord,valora) VALUES (%s,'%s','%s',%s,%s)" % (self.valor("sql_table"),id,fecha,hora,valord,valora) # }}}4 # Envío el query a la base de datos {{{4 # print sqlquery try: BD.execute(sqlquery) except MySQLdb.IntegrityError, message: # Entrada repetida (ni caso) if (message[0]!=1062): raise IOError,"SQL INTEGRITY ERROR: Error %d: %s [PELIGRO: Es posible que se haya producido una pedida de datos]" % (message[0],message[1]) else: pass except Exception, message: # Error en la operación raise IOError,"SQL OPERATIONAL ERROR: Error %d: %s [PELIGRO: Es posible que se haya producido una pedida de datos]" % (message[0], message[1] ) # }}}4 # Next one percent_control+=1 # }}}3 # Borro el fichero procesado {{{3 if (known_file): # Obtengo el nombre fichero="%s%s" % (self.valor("dirreg"),nombre) # Cierro el fichero file.close() # Ordeno el borrado self.debug("Borrando el fichero procesado") try: os.unlink(fichero) except Exception,e: listaborrar.append((nombre,e)) # }}}3 self.debug("") # Controlo cualquier excepción que se produzca except NoFile,error: self.debug(error) self.debug("") except LockError,e: self.debug("Fichero %s bloqueado por otro proceso, no se usa." % (nombre)) self.debug("") except IOError,e: #except Exception,e: print "OUT_DB_MYSQL::ERROR: Se produjo un error al cargar los datos del fichero %s en la Base de datos. (Error: %s)" % (nombre,e) # }}}2 # Cierro la conexión SQL sin importar errores {{{2 self.debug("Cerrando conexion a la Base de Datos...") try: self.debug(" Cerrando cursor de la Base de Datos (BD)") BD.close() self.debug(" Cerrando conexion a la Base de Datos (BDconnection)") BDconnection.close() except Exception,e: raise IOError,"SQL ERROR: al cerrar la conexión a la Base de Datos: %s" % (e[1]) #}}}2 # Muestro un listado de ficheros que no se pudieron borrar {{{2 for dato in listaborrar: (fichero,e)=dato self.debug("No se pudo borrar el fichero '%s': %s" % (fichero,e)) # }}}2 # }}}1 # Busca si la señal esta en la lista de señales y la devuelve formateada {{{1 ## \internal ## Busca si la senal esta en la lista de senales del Telemando indicado y la devuelve formateada: \n ## \param self - ## \param telemando Telemando en el que buscar la senal ## \param datos Datos para la busqueda requiere (senalbusqueda,fecha,valor): donde senal busqueda es el nombre del conector que localizar, la fecha y el valor son valores que se mantendran en la tupla entregada ## \return Devuelve una lista con los elementos encontrados (id,fecha,tipo,valor): tipo es 'a' analogico o 'd' para digital # # Formato de las señales obtenidas del Telemando: # id = Valor númerico (único) # alfanum = Valor alfanumerico (único) -> Ayuda al operador de la config # telemando = Telemando al que hacer referencia # Descripcion = Texto de la señal # Tipo = 'a':analógica o 'd':digital (la digitalización es aplicable a cualquier señal) # Fisica = Conexión física (campo del que extraer la señal) # Control = Tipo de Telemando ("pesyr","momentum","netstatistics",...) # # + PESYR: \n # Bit = Bit que usar en la lectura despues de la digitalización (no usado con tipo='a') \n # Algoritmo = 'hex':número hexadecimal , 'dec':número decimal (no usado con tipo='a') \n # # + MOMENTUM: \n # Logico = Tupla de 2 elementos con inicio y fin de los valores lógicos posibles \n # Fisico = Tupla de 2 elementos con inicio y fin de los valores físicos posibles \n # # + NETSTATISTICS: \n # No hay más parametros útils def __buscador(self,telemando,tipo,senales,(senalbusqueda,fecha,valor)): # Cacheo algunas funciones importantes fis2log_cache=fis2log type_cache=type len_cache=len # Localizo en local la señal indicada encontradas=[] for senal in senales: # Quita el tipo de dato de la cola tipo_pos=senal[5].split(":") if (len_cache(tipo_pos)==3): senalpos="%s:%s" % (tipo_pos[0],tipo_pos[1]) else: if (tipo=="advantech"): posicion=tipo_pos[1] slotpin=posicion.split(",") if (len(slotpin)==2): (slot,pin)=slotpin posicion=int(self.adamposition(slot,pin,(senal[4]=='a'))) senalpos="%s:%s" % (tipo_pos[0],posicion) else: senalpos=senal[5] # Si encuentro la señal if (senalpos==senalbusqueda): # Formateo los datos según indica la señal if (tipo=="pesyr"): # Según el tipo de señal if (valor==None): valortemp=None else: # Reviso si es analogica if (senal[4]=='a'): # Recojo los transformadores logico=senal[6] fisico=senal[7] # Compruebo sus valores if ((logico!=None) and (fisico!=None)): # Compruebo los tipos if ((type_cache(())!=type_cache(logico)) or (len_cache(logico)!=2)): raise IOError,"La senal %s del Telemando %s es incorrecta, el valor logico debe ser una lista: (inicio,fin)" % (senalbusqueda,telemando) if ((type_cache(())!=type_cache(fisico)) or (len_cache(fisico)!=2)): raise IOError,"La senal %s del Telemando %s es incorrecta, el valor fisico debe ser una lista: (inicio,fin)" % (senalbusqueda,telemando) # Aplico la funcion de cambio valortemp=fis2log_cache(logico[0],logico[1],fisico[0],fisico[1],valor) else: # Devuelvo el valor sin transformar valortemp=valor else: valortemp=valor # Creo el resultado encontradas.append((senal[0],fecha,senal[4],valortemp)) elif (tipo=="momentum"): if (valor==None): valortemp=None elif (senal[4]=='a'): # Recojo los valores logico=senal[6] # Valor que representa en la realidad fisico=senal[7] # Valor recogido del autómata (0,32000 generalmente) # Ajusto los valores if (logico==None): logico=(0.0,32768.0) if (fisico==None): fisico=(0.0,32768.0) # Control de valores if ((type_cache(())!=type_cache(logico)) or (len_cache(logico)!=2)): raise IOError,"La senal %s del Telemando %s es incorrecta, el valor logico debe ser una lista: (inicio,fin)" % (senalbusqueda,telemando) if ((type_cache(())!=type_cache(fisico)) or (len_cache(fisico)!=2)): raise IOError,"La senal %s del Telemando %s es incorrecta, el valor fisico debe ser una lista: (inicio,fin)" % (senalbusqueda,telemando) # Aplico la funcion de cambio (fisico=Del automata, logico=Valor que representa) valortemp=fis2log_cache(logico[0],logico[1],fisico[0],fisico[1],valor) else: valortemp=valor # Creo el resultado encontradas.append((senal[0],fecha,senal[4],valortemp)) elif (tipo=="advantech"): if (valor==None): valortemp=None elif (senal[4]=='a'): # Recojo los valores logico=senal[6] # Valor que representa en la realidad fisico=senal[7] # Valor recogido del autómata (0,32000 generalmente) # Ajusto los valores if (logico==None): logico=(0.0,65535.0) if (fisico==None): fisico=(0.0,65535.0) # Control de valores if ((type_cache(())!=type_cache(logico)) or (len_cache(logico)!=2)): raise IOError,"La senal %s del Telemando %s es incorrecta, el valor logico debe ser una lista: (inicio,fin)" % (senalbusqueda,telemando) if ((type_cache(())!=type_cache(fisico)) or (len_cache(fisico)!=2)): raise IOError,"La senal %s del Telemando %s es incorrecta, el valor fisico debe ser una lista: (inicio,fin)" % (senalbusqueda,telemando) # Aplico la funcion de cambio (fisico=Del automata, logico=Valor que representa) valortemp=fis2log_cache(logico[0],logico[1],fisico[0],fisico[1],valor) else: valortemp=valor # Creo el resultado encontradas.append((senal[0],fecha,senal[4],valortemp)) elif (tipo=="netstatistics"): # Recojo el valor valortemp=valor # Creo el resultado encontradas.append((senal[0],fecha,senal[4],valortemp)) elif (tipo=="web"): # Recojo el valor valortemp=valor # Creo el resultado encontradas.append((senal[0],fecha,senal[4],valortemp)) else: raise IOError,"Tipo incorrecto, se indico el tipo: %s" % (tipo) # Devuelvo el resultado return encontradas # }}}1 # Localizo el elemento basedatos-anterior y el elemento Basedatos-posterior {{{1 ## Localizo el elemento basedatos-anterior y el elemento Basedatos-posterior def _anterior_posterior(self,id,fecha_ini,hora_ini,fecha_fin,hora_fin): # Inicializa los valores {{{2 dato_ini=None dato_med=None dato_fin=None # }}}2 # Localiza el ID de la señal y el tipo {{{2 senal=self.__tablasenales.search(id) (id,name,telemando,descripcion,tipo,conector,param1,param2)=senal if (id==None): raise IOError,"La senal entregada no se puede localizar en la lista de senales: Esto es un error de programacion." # }}}2 # Decido el tipo de query a realizar {{{2 if (tipo=='d'): tipo_query="valord" else: tipo_query="valora" # }}}2 # Localizo el elemento basedatos-anterior {{{2 # Creo el query query="" query="%s%s" % (query,"SELECT fecha,hora,%s AS valor " % (tipo_query)) query="%s%s" % (query,"FROM datos WHERE id=%s " % (id)) query="%s%s" % (query,"AND ((fecha='%s' AND hora<'%s') OR fecha<'%s') " % (fecha_ini,hora_ini,fecha_ini)) query="%s%s" % (query,"ORDER BY fecha DESC,hora DESC,server DESC LIMIT 1") # Envío el query a la base de datos #self.debug("Enviando QUERY: %s" % (query)) if ((self.__BD==None) or (self.__BDconnection==None)): raise IOError,"DB_MYSQL: Se intentó un QUERY pero el sistema no esta correctamente conectado a la Base de Datos (%s)" % (query) try: self.__BD.execute(query) self.__DBresult=self.__BD.rowcount except MySQLdb.OperationalError, message: # No hubo resultados de la busqueda self.debug("No se han encontrado resultados") self.__DBresult=None except Exception, message: # Error en la operación raise IOError,"DB_MYSQL OPERATIONAL ERROR: Error %d: %s\n(QUERY: %s)" % (message[0],message[1],query) # Compruebo que se encontró el resultado if ((self.__DBresult!=None) and (self.__DBresult>0)): # Extraigo todos todos=self.__BD.fetchall() # Extraigo el primero pieza=None for pieza in todos: break; # Prepara la fecha fecha=str(pieza[0]) (a,m,d)=fecha.split("-") fecha="%s-%s-%s" % (a,digitos(m,2),digitos(d,2)) # Prepara la hora hora=str(pieza[1]) (h,m,s)=hora.split(":") hora="%s:%s:%s" % (digitos(h,2),digitos(m,2),digitos(s,2)) # Prepara el valor (normalizado) valor=normalizar_float(pieza[2]) # Creo la nueva fecha dato_ini=(id,fecha,hora,valor,tipo) # Anota que no queda nada por leer self.__DBresult=None else: # Devolver nada dato_ini=None # }}}2 # Localizo el elemento basedatos-posterior {{{2 # Creo el query query="" query="%s%s" % (query,"SELECT fecha,hora,%s AS valor " % (tipo_query)) query="%s%s" % (query,"FROM datos WHERE id=%s " % (id)) query="%s%s" % (query,"AND ((fecha='%s' AND hora>'%s') OR fecha>'%s') " % (fecha_fin,hora_fin,fecha_fin)) query="%s%s" % (query,"ORDER BY fecha ASC,hora ASC,server ASC LIMIT 1") # Envío el query a la base de datos #self.debug("Enviando QUERY: %s" % (query)) if ((self.__BD==None) or (self.__BDconnection==None)): raise IOError,"DB_MYSQL: Se intentó un QUERY pero el sistema no esta correctamente conectado a la Base de Datos (%s)" % (query) try: self.__BD.execute(query) self.__DBresult=self.__BD.rowcount except MySQLdb.OperationalError, message: # No hubo resultados de la busqueda self.debug("No se han encontrado resultados") self.__DBresult=None except Exception, message: # Error en la operación raise IOError,"DB_MYSQL OPERATIONAL ERROR: Error %d: %s\n(QUERY: %s)" % (message[0],message[1],query) # Compruebo que se encontró el resultado if ((self.__DBresult!=None) and (self.__DBresult>0)): # Extraigo todos todos=self.__BD.fetchall() # Extraigo el primero pieza=None for pieza in todos: break; # Prepara la fecha fecha=str(pieza[0]) (a,m,d)=fecha.split("-") fecha="%s-%s-%s" % (a,digitos(m,2),digitos(d,2)) # Prepara la hora hora=str(pieza[1]) (h,m,s)=hora.split(":") hora="%s:%s:%s" % (digitos(h,2),digitos(m,2),digitos(s,2)) # Prepara el valor (normalizado) valor=normalizar_float(pieza[2]) # Creo la nueva fecha dato_fin=(id,fecha,hora,valor,tipo) # Anota que no queda nada por leer self.__DBresult=None else: dato_fin=None # }}}2 # Devolvemos los datos anterior y posterior return (dato_ini,dato_fin) # }}}1 # Conversion of positions of memory for adam {{{1 ## Conversion of positions of memory for adam ## \param self - ## \param slot Slot number ## \param pin Pin number ## \param da Is analogic? (Default: False) def adamposition(self,slot,pin,analogic=False): # Get the number of pin per slot if (analogic): slot_pins=8 else: slot_pins=16 # Calculate the position of memory return (slot_pins*int(slot)+int(pin)) # }}}1 # EXCEPTION CLASSES {{{1 # Excepciones básicas # Except (General Exception) {{{2 class Except(Exception): def __init__(self,string): self.string=string def __str__(self): return self.string #}}}2 # ExecutionError (Error de ejecucción) {{{2 class ExecutionError(Exception): def __init__(self,string): self.string=string def __str__(self): return self.string # }}}2 # NoFile (No existe el fichero) {{{2 class NoFile(Exception): def __init__(self,string): self.string=string def __str__(self): return self.string # }}}2 # }}}1