class Dosificadora:
    def __init__(self):
        #Crear el objeto de la clase dosificadora

        ##Convenciones: axxxx: a significa atributo
        #Con-> Concentrado
        #Min-> Mineral
        #Lev-> Levadura

        #Puertos de control para las valvulas
        self.avTolva = 2
        self.avMineral = 16
        self.avLevadura = 27

        #Puertos de asignacion de las celdas de carga
        self.alsensorC1 = (11, 9)  #Formato tupla: (dt,sck)
        self.alsensorC2 = (22, 10)
        self.alsensorC3 = (24, 23)
        self.alsensorC4 = (12, 6)
        self.alsensorML = (19, 13)

        #Puertos control de motores
        self.amCon = (7, 8)  #Formato tupla: (Encendido, velocidad)
        self.amMin = (20, 21)  #Formato tupla: (velocidad, sentido)
        self.amLev = (25, 26)

        #Sensibilidades celdas de carga
        self.asMin = 1030.3320
        self.asLev = 2563.3821
        self.asC1 = 1
        self.asC2 = 1
        self.asC3 = 1
        self.asC4 = 1
        self.asConc = 53.2201

        #Valores de Tara para cada celda de carga
        self.asZeroMin = 0
        self.asZeroLev = 0
        self.asZeroC1 = 0
        self.asZeroC2 = 0
        self.asZeroC3 = 0
        self.asZeroC4 = 0

        #Masas objetivo
        self.aConObj = 1
        self.aMinObj = 1
        self.aLevObj = 1
        self.aMasaObj = [self.aConObj, self.aMinObj, self.aLevObj]

        #Parametros del filtro tamizador y media movil
        self.aPeso_kbuffer = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
                              [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
                              [0.0, 0.0, 0.0, 0.0, 0.0,
                               0.0]]  #Formato lista [Con,Min,Lev]
        self.aSk = [0.0, 0.0, 0.0]  #Formato listas [Con,Min,Lev]
        self.aContador = [0, 0, 0]
        self.aDato_k_1 = [0.0, 0.0, 0.0]
        self.aX_k_1 = [0.0, 0.0, 0.0]

        #Valores para algoritmo de control
        self.aMultiplo = [0.8, 0.8, 0.8]  #Formato listas [Con,Min,Lev]
        self.aDeltaRef = [0.0, 0.0, 0.0]

        self.aInt = [0.0, 0.0, 0.0]
        self.aKp = [0.00051743, 0.0, 0.0]
        self.aKi = [0.0, 0.0, 0.0]
        self.aKd = [0.00080848, 0.0, 0.0]
        self.aN = [0.3704]

        self.aVk_1 = 0.0
        self.aEk_1 = 0.0
        self.aYk_1 = 0.0
        self.aDk_1 = 0.0
        self.aUk_1 = 0
        self.aRk_1 = 0

        self.aInt_Retardo = 0
        self.aTcontador = 0
        self.aDoCalcularKp = [True, True, True]
        self.aPWM = [0.0, 0.0, 0.0]

        self.aAceleracion = [300.0, 0.0, 0.0]
        #Otros atributos
        self.asText = "________________"  #Separador de Textos
        self.minCon = 39.0  #Menor ciclo de PWM permitido para el concentrado
        self.maxCon = 99.0  #Mayor ciclo de PWM permitido para el concentrado
        self.razon = [
            60.0, 50.0, 10.0
        ]  #Mayor tasa de cambio permitida por el filtro tamizador
        #Formato lista [Con,Min,Lev]
        self.aConCrucero = 70.0  #Velocidad crucero motor Con
        self.aConMin = 60.0  #Minima velocidad para mover el motor

    def __del__(self):
        #Metodo destructor de objeto
        nombre = self.__class__.__name__
        print(nombre, "Destruido")

    def inicializarPuertos(self):
        #Encargado de iniciar el estado de los puertos de RPi.

        print("\n________________\nIniciando puertos\n________________\n")
        #Configurar puertos
        #Valvulas
        GPIO.setup(self.avTolva, GPIO.OUT)
        GPIO.setup(self.avMineral, GPIO.OUT)
        GPIO.setup(self.avLevadura, GPIO.OUT)

        #Motores
        #Concentrado
        GPIO.setup(self.amCon[0], GPIO.OUT)
        GPIO.setup(self.amCon[1], GPIO.OUT)

        #Mineral
        GPIO.setup(self.amMin[0], GPIO.OUT)
        GPIO.setup(self.amLev[0], GPIO.OUT)

        #Levadura
        GPIO.setup(self.amMin[1], GPIO.OUT)
        GPIO.setup(self.amLev[1], GPIO.OUT)

        #Colocar todos los puertos en BAJO "LOW".
        GPIO.output(self.avTolva, 0)
        GPIO.output(self.avMineral, 0)
        GPIO.output(self.avLevadura, 0)

        GPIO.output(self.amCon[0], 0)
        GPIO.output(self.amCon[1], 0)

        GPIO.output(self.amMin[0], 0)
        GPIO.output(self.amMin[1], 0)

        GPIO.output(self.amLev[0], 0)
        GPIO.output(self.amLev[1], 0)

    def inicializarMotores(self):
        #Iniciar el estado de los motores
        #Frecuencia de PWM
        self.amMinPWM = GPIO.PWM(self.amMin[0],
                                 300)  #Formato tupla: (velocidad, sentido)
        self.amLevPWM = GPIO.PWM(self.amLev[0],
                                 300)  #Formato tupla: (velocidad, sentido)
        self.amConPWM = GPIO.PWM(self.amCon[1], 250)

        ##Iniciar PWM en valor 0
        self.amMinPWM.start(0)
        self.amLevPWM.start(0)
        self.amConPWM.start(0)

    def inicializarCeldas(self):
        #Inciar celdas de carga
        print(
            "\n________________\nIniciando celdas de carga\n________________\n"
        )
        #Formato tupla: self.alsensorA	=	(dt,sck)

        #Celda de carga Concentrado C1
        self.ahxC1 = Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
        #Celda de carga Concentrado C2
        self.ahxC2 = Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
        #Celda de carga Concentrado C3
        self.ahxC3 = Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
        #Celda de carga Concentrado C4
        self.ahxC4 = Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)
        #Celda de carga Levadura Mineral
        self.ahxML = Scale(self.alsensorML[0], self.alsensorML[1], 1, 80)

        self.resetearCeldas()

    def encenderMotores(self, motor):
        #Metodo que activa los motores
        #Entrada: 	self-> 	Objeto propio de python
        #			motor->	Selector del motor:
        #					Con: Concentrado, Min: mineral Lev: levadura

        if (motor == 'Con'):
            if self.aConObj != 0:
                #Encendido motor Con
                velocidad = 99  #self.aConCrucero
                self.amConPWM.ChangeDutyCycle(velocidad)
                GPIO.output(self.amCon[0], 1)
            else:
                print("Masa es 0, concentrado no encendido")
            return

        if (motor == 'Min'):
            if self.aMinObj != 0:
                self.amMinPWM.ChangeFrequency(750)
                self.amMinPWM.ChangeDutyCycle(50)
            else:
                print("Masa igual a 0, mineral no encendido")
            return

        if (motor == 'Lev'):
            if self.aLevObj != 0:
                self.amLevPWM.ChangeFrequency(750)
                self.amLevPWM.ChangeDutyCycle(50)
            else:
                print("Masa igual a 0, levadura no encendido")
            return

        else:
            print("Motor no encontrado")

    def desacelerarMotores(self, motor):
        #Metodo que desacelera los motores
        if (motor == 'Con'):
            velocidad = self.aConMin
            self.amConPWM.ChangeDutyCycle(velocidad)
            return

        if (motor == 'Min'):
            self.amMinPWM.ChangeFrequency(200)
            self.amMinPWM.ChangeDutyCycle(50)
            return

        if (motor == 'Lev'):
            self.amLevPWM.ChangeFrequency(200)
            self.amLevPWM.ChangeDutyCycle(50)
            return

        else:
            print("Motor no encontrado")
            return

    def apagarMotores(self, motor, condicion):
        #Detener motores
        #Entradas: motor: 	Seleccion del motor deseado
        #					Con -> Concentrado
        #					Min -> Mineral
        #					Lev -> Levadura
        #		Condicion:	Indica si el motor no fue apagado en la iteracion anterior
        if (motor == 'Con'):
            GPIO.output(self.amCon[0], 0)
            self.amConPWM.stop()
            if condicion:
                print("Concentrado apagado")
            return

        if (motor == 'Min'):
            self.amMinPWM.ChangeFrequency(50)
            self.amMinPWM.ChangeDutyCycle(0)
            if condicion:
                print("Mineral apagado")
            return

        if (motor == 'Lev'):
            self.amLevPWM.ChangeFrequency(50)
            self.amLevPWM.ChangeDutyCycle(0)
            if condicion:
                print("Levadura apagado")
            return

        else:
            print("Motor no encontrado")
            return

    def abrirCerrarValvulas(self, valvula, condicion):
        #Metodo de abrir y cerrar valvulas
        #Entradas:	valvula:
        #				Tolv -> Puerta de la tolva Romana
        #				Min	->	Compuerta del mineral
        #				Lev ->	Compuerta levadura
        #			condicion:
        #				0 	->	Valvula cerrada
        #				1	->	Valvula abierta

        if (valvula == 'Tolv'):
            GPIO.output(self.avTolva, condicion)
            return

        if (valvula == 'Min'):
            GPIO.output(self.avMineral, condicion)
            return

        if (valvula == 'Lev'):
            GPIO.output(self.avLevadura, condicion)
            return

        else:
            print("Valvula incorrecta")

    def cambiarSensibilidad(self, celda, sensibilidad):
        #Metodo para cambiar la sensibilidad de la celda de carga: (depuracion)
        #Formato de celda: 'Min','Lev','A','B'
        #Entradas: celda: A1, A2, B1, B2, Min, Lev
        print("Cambiando sensibilidad")
        if (celda == 'A1'):
            self.asA1 = sensibilidad
            self.axA.select_channel(channel='A')
            self.axA.set_scale_ratio(sensibilidad)
            return

        if (celda == 'A2'):
            self.asA2 = sensibilidad
            self.axA.select_channel(channel='B')
            self.axA.set_scale_ratio(sensibilidad)
            return

        if (celda == 'B1'):
            self.asB1 = sensibilidad
            self.axB.select_channel(channel='A')
            self.axB.set_scale_ratio(sensibilidad)
            return

        if (celda == 'B2'):
            self.asB2 = sensibilidad
            self.axB.select_channel(channel='B')
            self.axB.set_scale_ratio(sensibilidad)
            return

        if (celda == 'Min'):
            self.asMin = sensibilidad
            self.axML.select_channel(channel='A')
            self.axML.set_scale_ratio(sensibilidad)
            return

        if (celda == 'Lev'):
            self.asLev = sensibilidad
            self.axML.select_channel(channel='A')
            self.axML.set_scale_ratio(sensibilidad)
            return

        else:
            print("Celda no encontrada")

    def leerMineral(self, lecturas):
        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar

        #Mineral puerto A del sensor
        masaMin = -((self.ahxML.weighOnce()) - self.asZeroMin) / self.asMin
        return masaMin

    def leerLevadura(self, lecturas):
        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        masaLev = (self.ahxML.weighOnce() - self.asZeroLev) / self.asLev
        return masaLev

    def leerConcentrado(self, lecturas):
        #Leer el peso del concentrado en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar

        Conc1 = self.ahxC1.weighOnce() - self.asZeroC1

        Conc2 = self.ahxC2.weighOnce() - self.asZeroC2

        Conc3 = self.ahxC3.weighOnce() - self.asZeroC3

        Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)

        Conc = (Conc1 + Conc2 + Conc3 + Conc4) / (4 * self.asConc)
        #Nota: De momento se estan leyendo solo las celdas de los puertos A del concentrado
        #		las celdas B presetan problemas de retardos en las lecturas
        return float(Conc)

    def cerrarSteppers(self):
        #Metodo para apagar puertos de velocidad de los motores
        self.amMinPWM.stop()
        self.amLevPWM.stop()
        self.amConPWM.stop()

    def leer4Concentrado(self):
        #Metodo para leer por separado cada celda de carga del concentrado 	(depuracion)
        Conc1 = self.ahxC1.weighOnce() - self.asZeroC1

        Conc2 = self.ahxC2.weighOnce() - self.asZeroC2

        Conc3 = self.ahxC3.weighOnce() - self.asZeroC3

        Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)
        print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4))

    def leer4ConcentradoRaw(self, lecturas):
        #Metodo para leer cada celda del concentrado sin restar tara 		(depuracion)
        Conc1 = self.ahxC1.weighOnce()

        Conc2 = self.ahxC2.weighOnce()

        Conc3 = self.ahxC3.weighOnce()

        Conc4 = self.ahxC4.weighOnce()

        print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4))

    def tararConcentrado(self, imprimir=False, lecturas=30):
        #Metodo para tarar los amplificadores del concentrado
        if imprimir:
            print("Tarando")

        self.asZeroC1 = self.ahxC1.weigh(80)

        self.asZeroC2 = self.ahxC2.weigh(80)

        self.asZeroC3 = self.ahxC3.weigh(80)

        self.asZeroC4 = self.ahxC4.weigh(80)

        if imprimir:
            print("Tara del concentrado\n%d\t%d\t%d\t%d\t" %
                  (self.asZeroC1, self.asZeroC2, self.asZeroC3, self.asZeroC4))

    def tararMineral(self, printVal=False, lecturas=30):
        #Metodo para tarar mineral

        self.asZeroMin = self.ahxML.tare(lecturas, False)
        if printVal:
            print("\tTara del mineral %d" % (self.asZeroMin))

    def tararLevadura(self, printVal=False, lecturas=30):
        #Metodo para tarar levdura
        self.asZeroMin = self.ahxML.tare(lecturas, False)
        if printVal:
            print("\tTara de la levadura %d" % (self.asZeroMin))

    def filtradorTamizador(self, dato, alimento):
        #Metodo para filtrar y tamizar los valores de las celdas de carga
        #Se aplica un filtro de media movil con tres periodos,
        #luego se eliminan las lecturas que presenten cambios abruptos respecto de los valores predecesores.
        if alimento == 'Con':

            #Tamizar
            if ((abs(dato - self.aDato_k_1[0])) > self.razon[0]):
                datoT = self.aX_k_1[0]
                #print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[0] = self.aSk[0] - self.aPeso_kbuffer[0][
                self.aContador[0]] + datoT
            concentrado = self.aSk[0] / 5
            self.aPeso_kbuffer[0][self.aContador[0]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[0] += 1
            self.aDato_k_1[0] = dato
            self.aX_k_1[0] = datoT

            if self.aContador[0] == 5:
                self.aContador[0] = 0
            return concentrado

        if alimento == 'Min':
            #Tamizar
            if ((abs(dato - self.peso_k_1[1])) > self.razon[1]):
                datoT = self.peso_k_1[1]
                print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[1] = self.aSk[1] - self.aPeso_kbuffer[1][
                self.aContador[1]] + datoT
            mineral = self.aSk[1] / 5
            self.aPeso_kbuffer[1][self.aContador[1]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[1] += 1
            self.aDato_k_1[1] = dato
            self.aX_k_1[1] = datoT

            if self.aContador[1] == 5:
                self.aContador[1] = 0
            return mineral

        if alimento == 'Lev':
            #Tamizar
            if ((abs(dato - self.aDato_k_1[2])) > self.razon[2]):
                datoT = self.aX_k_1[2]
                #print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[2] = self.aSk[2] - self.aPeso_kbuffer[2][
                self.aContador[2]] + datoT
            levadura = self.aSk[2] / 5
            self.aPeso_kbuffer[2][self.aContador[2]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[2] += 1
            self.aDato_k_1[2] = dato
            self.aX_k_1[2] = datoT

            if self.aContador[2] == 5:
                self.aContador[2] = 0
            return levadura

        else:
            print("Alimento no encontrado")

    def inRangeCoerce(self, dato, minimo=0.0, maximo=100.0):
        #Metodo que limita los valores de una variable
        if dato > maximo:
            return maximo

        if dato < minimo:
            return minimo

        else:
            return dato

    def normalizarVelocidadConcentrado(self, dato):
        #Metodo para normalizar los valores del concentrado
        #Debido a la electronica, el valor de PWM permitido es entre 39 y 99.
        #Fuera de esos valores comienza a presentarse comportamiento erratico.
        dato = self.inRangeCoerce(dato, 0, 100)
        dato = (self.maxCon - self.minCon) / 100 * dato + self.minCon
        return dato

    #Metodos para resumir bloques de la secuencia
    def tararCeldas(self):
        #Metodo para tarar todas las cedas de carga. Permite no hacerlo desde el main
        self.leerMineral(80)
        print("________________\nTarando Concentrado\n________________\n")
        self.tararConcentrado(80, True)
        print("Zero A1 ", self.asZeroC1)
        print("Zero A2 ", self.asZeroC2)
        print("Zero B1 ", self.asZeroC3)
        print("Zero B2 ", self.asZeroC4)

        print("________________\nTarando Mineral\n________________\n")
        self.tararMineral(80)
        print("________________\nTarando Levadura\n________________\n")
        self.tararLevadura(80)

    def resetearCeldas(self):
        print("Reseteando celdas de carga concentrado")
        #Celdas del concentrado
        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC2.turnOff()
        time.sleep(0.5)
        self.ahxC2.turnOn()
        time.sleep(0.5)

        self.ahxC3.turnOff()
        time.sleep(0.5)
        self.ahxC3.turnOn()
        time.sleep(0.5)

        self.ahxC4.turnOff()
        time.sleep(0.5)
        self.ahxC4.turnOn()
        time.sleep(0.5)

        print("Reseteando celdas de carga Mineral y Levadura")
        self.ahxML.turnOff()
        time.sleep(0.5)
        self.ahxML.turnOn()
        time.sleep(0.5)

    def filtroButterWorth(self, xk):
        self.yk = (
            0.7769 * self.xk_1  #- 0.007079*self.xk_2
            + 0.2231 * self.yk_1)  #- 0.000002 * self.yk_2)
        #Retrasar muestras
        #self.xk_4	= self.xk_3
        #self.xk_3	= self.xk_2
        self.xk_2 = self.xk_1
        self.xk_1 = xk

        #self.yk_4	= self.yk_3
        #self.yk_3	= self.yk_2
        self.yk_2 = self.yk_1
        self.yk_1 = self.yk
        return self.yk

    def controlPD(self, yk):
        #Comienza algoritmo de control

        #Estimacion de la velocidad
        #vk = yk-self.aYk_1+0.6703*self.aVk_1

        #Estimacion del retardo

        #self.aInt_Retardo = self.inRangeCoerce(self.aInt_Retardo,0,10000)

        #Estimacion del error
        ek = (self.aMasaObj[0] - yk)
        #Estimacion del control PD
        pk = self.aKp[0] * (ek)
        dk = ((self.aKd[0] * self.aN[0]) *
              (ek - self.aEk_1) + self.aDk_1) / (1 + self.aN[0] * 0.05)
        pidk = pk + dk + 0.4
        #Compensacion de componente no lineal
        pidk = 100 * self.inRangeCoerce(pidk, 0, 0.99)
        self.amConPWM.ChangeDutyCycle(pidk)
        #Termina algoritmo de control

        #Retrasa las muestras
        self.aYk_1 = yk
        self.aDk_1 = dk
        self.aEk_1 = ek
        self.aPWM[0] = pidk

    def generadorTrayectoria(self):
        print("Generando Trayectorias")
        a = self.aAceleracion[0]
        t2 = self.aMasaObj[0] / a - 2
        tf = t2 + 4
        k = 0
        m1 = 0.5 * a
        m2 = a * t2 + m1
        tiempo = np.arange(0.05, 30.05, 0.05)
        k = 0
        mk = np.zeros(600)

        #Inicia la generacion de la trayectoria
        t = 0
        while t < 30:
            t = tiempo[k]
            if t <= 1:
                mk[k] = (a / 2) * t * t
            if ((1 < t) and (t <= (t2 + 1))):
                mk[k] = a * (t - 1) + m1
            if ((t2 + 1 < t) and (t <= tf)):
                mk[k] = -a / 6 * (t - t2 - 1) * (t - t2 - 1) + a * (t - t2 -
                                                                    1) + m2
            elif (t > tf):
                mk[k] = self.aMasaObj[0]
            k += 1
        print("Trayectoria generada")
        self.aTrayectoriaCon = mk
Exemplo n.º 2
0
class Testing:
    def __init__(self):
        #Atributos de las celdas de carga del concentrado
        self.alsensorC1 = (11, 9)  #Formato tupla: (dt,sck)
        self.alsensorC2 = (22, 10)
        self.alsensorC3 = (24, 23)
        self.alsensorC4 = (12, 6)
        self.alsensorML = (19, 13)

        self.asZeroC1 = 0
        self.asZeroC1 = 0
        self.asZeroC1 = 0
        self.asZeroC1 = 0
        self.asZeroMin = 0
        self.asZeroLev = 0

        self.asConc = 53.2201
        self.asMin = 1030.3320
        self.asLev = 2563.3821

        #Atributos del filtro
        self.aDato_k_1 = [0.0, 0.0, 0.0]
        self.razon = [60.0, 50.0, 10.0]
        self.aX_k_1 = [0.0, 0.0, 0.0]

        self.aSk = [0.0, 0.0, 0.0]
        self.aPeso_kbuffer = np.zeros((3, 20), dtype=np.float32)
        self.aContador = [0, 0, 0]

        #Otros atributos
        self.aEspacio = "_________________"

    def __del__(self):
        nombre = self.__class__.__name__
        print(nombre, "Destruido")

    def InicializarCeldasTR(self):
        #Inicializa las celdas de carga del concentrado
        self.ahxC1 = Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
        #Celda de carga Concentrado C2
        self.ahxC2 = Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
        #Celda de carga Concentrado C3
        self.ahxC3 = Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
        #Celda de carga Concentrado C4
        self.ahxC4 = Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)

    def InicializarCeldasML(self):
        #Inicializa las celdas de carga del mineral Levadura
        self.ahxML = Scale(self.alsensorML[0], self.alsensorML[1], 1, 80)

    def ResetearCeldasTR(self):
        print("Reseteando celdas de carga concentrado")
        #Celdas del concentrado
        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC2.turnOff()
        time.sleep(0.5)
        self.ahxC2.turnOn()
        time.sleep(0.5)

        self.ahxC3.turnOff()
        time.sleep(0.5)
        self.ahxC3.turnOn()
        time.sleep(0.5)

        self.ahxC4.turnOff()
        time.sleep(0.5)
        self.ahxC4.turnOn()
        time.sleep(0.5)

    def ResetearCeldasML(self):
        print("Reseteando celdas de carga concentrado")
        #Celdas del concentrado
        self.ahxML.turnOff()
        time.sleep(0.5)
        self.ahxML.turnOn()

    def LeerConcentrado(self, lecturas):
        #Leer el peso del concentrado en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        concentrado = 0
        for i in range(lecturas):
            Conc1 = self.ahxC1.weighOnce() - self.asZeroC1

            Conc2 = self.ahxC2.weighOnce() - self.asZeroC2

            Conc3 = self.ahxC3.weighOnce() - self.asZeroC3

            Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)

            Conc = (Conc1 + Conc2 + Conc3 + Conc4) / (4 * self.asConc)
            concentrado += Conc
        concentrado = concentrado / lecturas
        return float(concentrado)

    def LeerMineral(self, lecturas):

        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        #Mineral puerto A del sensor
        masaMin = -((self.ahxML.weighOnce()) - self.asZeroMin) / self.asMin
        return masaMin

    def FiltroMediaTamizador(self, dato, alimento, periodos=5):
        #Metodo para filtrar y tamizar los valores de las celdas de carga
        #Se aplica un filtro de media movil con n periodos,
        #luego se eliminan las lecturas que presenten cambios abruptos respecto de los valores predecesores.
        if alimento == 'Con':

            #Tamizar
            if ((abs(dato - self.aDato_k_1[0])) > self.razon[0]):
                datoT = self.aX_k_1[0]
                #print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[0] = self.aSk[0] - self.aPeso_kbuffer[0][
                self.aContador[0]] + datoT
            concentrado = self.aSk[0] / periodos
            self.aPeso_kbuffer[0][self.aContador[0]] = datoT
            #Calcular filtro de media movil en linea
            self.aContador[0] += 1
            self.aDato_k_1[0] = dato
            self.aX_k_1[0] = datoT

            if self.aContador[0] == periodos:
                self.aContador[0] = 0
            return concentrado

        if alimento == 'Min':
            #Tamizar
            if ((abs(dato - self.peso_k_1[1])) > self.razon[1]):
                datoT = self.peso_k_1[1]
                print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[1] = self.aSk[1] - self.aPeso_kbuffer[1][
                self.aContador[1]] + datoT
            mineral = self.aSk[1] / periodos
            self.aPeso_kbuffer[1][self.aContador[1]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[1] += 1
            self.aDato_k_1[1] = dato
            self.aX_k_1[1] = datoT

            if self.aContador[1] == periodos:
                self.aContador[1] = 0
            return mineral

        if alimento == 'Lev':
            #Tamizar
            if ((abs(dato - self.aDato_k_1[2])) > self.razon[2]):
                datoT = self.aX_k_1[2]
                #print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[2] = self.aSk[2] - self.aPeso_kbuffer[2][
                self.aContador[2]] + datoT
            levadura = self.aSk[2] / periodos
            self.aPeso_kbuffer[2][self.aContador[2]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[2] += 1
            self.aDato_k_1[2] = dato
            self.aX_k_1[2] = datoT

            if self.aContador[2] == periodos:
                self.aContador[2] = 0
            return levadura

        else:
            print("Alimento no encontrado")

    def TararConcentrado(self, imprimir=False, lecturas=30):
        #Metodo para tarar los amplificadores del concentrado
        if imprimir:
            print("Tarando")

        self.asZeroC1 = self.ahxC1.weigh(lecturas)

        self.asZeroC2 = self.ahxC2.weigh(lecturas)

        self.asZeroC3 = self.ahxC3.weigh(lecturas)

        self.asZeroC4 = self.ahxC4.weigh(lecturas)

        if imprimir:
            print("Tara del concentrado\n%d\t%d\t%d\t%d\t" %
                  (self.asZeroC1, self.asZeroC2, self.asZeroC3, self.asZeroC4))

    def ProbarCeldasTR(self, tiempo):
        #Obtiene muestras de la celda de carga por una cantidad determinada de tiempo
        print(self.aEspacio)
        print("Probando celdas de carga Tolva Romana")
        print(self.aEspacio)

        self.InicializarCeldasTR()
        self.ResetearCeldasTR()
        self.TararConcentrado(False, 80)

        print("Sin filtro\tCon Filtro")
        tic = time.time()
        while True:
            Con = self.LeerConcentrado(4)

            #Calcular filtro de media movil en linea
            ConF = self.FiltroMediaTamizador(Con, 'Con', 5)
            print("%f\t%f" % (Con, ConF))
            toc = time.time()

            #Condicion de parada para el ciclo
            if ((toc - tic) >= tiempo):
                break

    def ProbarCeldasMinLev(self, tiempo):
        #Obtiene muestras de la celda de carga por una cantidad determinada de tiempo
        print(self.aEspacio)
        print("Probando celdas de carga Mineral-Levadura")
        print(self.aEspacio)

        self.InicializarCeldasML()
        self.ResetearCeldasML()
        self.TararMinLev(False, 80)

        print("Sin filtro\tCon Filtro")
        tic = time.time()
        while True:
            Min = self.LeerMineral(80)

            #Calcular filtro de media movil en linea
            ConF = self.FiltroMediaTamizador(Con, 'Con', 5)
            print("%f\t%f" % (Con, ConF))
            toc = time.time()

            #Condicion de parada para el ciclo
            if ((toc - tic) >= tiempo):
                break
class Dosificadora:
	def __init__(self):
		#Crear el objeto de la clase dosificadora	

		##Convenciones: axxxx: a significa atributo
		 			#Con-> Concentrado
					#Min-> Mineral
					#Lev-> Levadura
						
		#Puertos de control para las valvulas
		self.avTolva	 	= 2
		self.avMineral 		= 16
		self.avLevadura 	= 27
		
		#Puertos de asignacion de las celdas de carga
		self.alsensorC1		= (11,9) 	#Formato tupla: (dt,sck)
		self.alsensorC2		= (22,10)	
		self.alsensorC3		= (24,23)
		self.alsensorC4		= (12,6)
		self.alsensorML		= (19,13)
		
		#Puertos control de motores
		self.amCon			= (7,8)		#Formato tupla: (Encendido, velocidad)
		self.amMin			= (20,21) 	#Formato tupla: (velocidad, sentido)
		self.amLev			= (25,26)
		
		#Sensibilidades celdas de carga
		self.asMin	 		= 1030.3320
		self.asLev		 	= 2563.3821
		self.asC1			= 1
		self.asC2			= 1
		self.asC3			= 1
		self.asC4			= 1
		self.asConc			= 53.2201
		
		#Valores de Tara para cada celda de carga
		self.asZeroMin		= 0
		self.asZeroLev		= 0
		self.asZeroC1		= 0
		self.asZeroC2		= 0
		self.asZeroC3		= 0
		self.asZeroC4		= 0
		
		#Masas objetivo
		self.aConObj		= 1
		self.aMinObj		= 1
		self.aLevObj		= 1
		
		#Parametros del filtro tamizador y media movil
		self.aPeso_kbuffer 	= [[0.0,0.0,0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0,0.0,0.0]]	#Formato lista [Con,Min,Lev]
		self.aSk			= [0.0,	0.0,0.0	] 		#Formato listas [Con,Min,Lev]
		self.aContador		= [0,	0,	0	]
		self.aDato_k_1		= [0,	0,	0	]
		self.aX_k_1			= [0,	0,	0	]
		
		#Otros atributos
		self.asText			= "________________" 	#Separador de Textos
		self.minCon			= 39.0					#Menor ciclo de PWM permitido para el concentrado
		self.maxCon			= 99.0					#Mayor ciclo de PWM permitido para el concentrado
		self.razon			= [60.0,50.0,10.0]			#Mayor tasa de cambio permitida por el filtro tamizador
													#Formato lista [Con,Min,Lev]
		self.aConCrucero	= 70.0					#Velocidad crucero motor Con
		self.aConMin		= 60.0					#Minima velocidad para mover el motor

		#Parametros filtro Butterworth
		"""self.xk 			=  0.0
		self.xk_1 			=  0.0
		self.xk_2			=  0.0
		self.xk_3			=  0.0
		self.xk_4 			=  0.0	
		
		self.yk 			=  0.0
		self.yk_1 			=  0.0
		self.yk_2 			=  0.0
		self.yk_3 			=  0.0
		self.yk_4 			=  0.0	
		"""
		#Control PI
		self.ik				=  0.0
		self.kp				=  0.003625
		self.ki				=  0.0003 #0.00065
		self.ref		=  0.0
	def __del__(self):
	#Metodo destructor de objeto
		nombre = self.__class__.__name__
		print(nombre, "Destruido")
		
	def inicializarPuertos(self):
		#Encargado de iniciar el estado de los puertos de RPi.
		
		print("\n________________\nIniciando puertos\n________________\n")
	#Configurar puertos
		#Valvulas
		GPIO.setup(self.avTolva,GPIO.OUT)
		GPIO.setup(self.avMineral,GPIO.OUT)
		GPIO.setup(self.avLevadura,GPIO.OUT)
			
		#Motores
			#Concentrado
		GPIO.setup(self.amCon[0],GPIO.OUT)
		GPIO.setup(self.amCon[1],GPIO.OUT)
			
			#Mineral
		GPIO.setup(self.amMin[0],GPIO.OUT)
		GPIO.setup(self.amLev[0],GPIO.OUT)
		
			#Levadura
		GPIO.setup(self.amMin[1],GPIO.OUT)
		GPIO.setup(self.amLev[1],GPIO.OUT)
		
		#Colocar todos los puertos en BAJO "LOW".
		GPIO.output(self.avTolva,0)
		GPIO.output(self.avMineral,0)
		GPIO.output(self.avLevadura,0)
		
		GPIO.output(self.amCon[0],0)
		GPIO.output(self.amCon[1],0)
		
		GPIO.output(self.amMin[0],0)
		GPIO.output(self.amMin[1],0)
		
		GPIO.output(self.amLev[0],0)
		GPIO.output(self.amLev[1],0)
		
	def inicializarMotores(self):
	#Iniciar el estado de los motores 
		#Frecuencia de PWM
		self.amMinPWM	= GPIO.PWM(self.amMin[0],300) 	#Formato tupla: (velocidad, sentido)
		self.amLevPWM	= GPIO.PWM(self.amLev[0],300)	#Formato tupla: (velocidad, sentido)
		self.amConPWM	= GPIO.PWM(self.amCon[1],250)
		
		##Iniciar PWM en valor 0
		self.amMinPWM.start(0)
		self.amLevPWM.start(0)
		self.amConPWM.start(0)
		
	def inicializarCeldas(self):
	#Inciar celdas de carga
		print("\n________________\nIniciando celdas de carga\n________________\n")
			#Formato tupla: self.alsensorA	=	(dt,sck)
			
			#Celda de carga Concentrado C1
		self.ahxC1 	= Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
			#Celda de carga Concentrado C2
		self.ahxC2 	= Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
			#Celda de carga Concentrado C3
		self.ahxC3 	= Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
			#Celda de carga Concentrado C4
		self.ahxC4 	= Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)		
			#Celda de carga Levadura Mineral
		self.ahxML	= Scale(self.alsensorML[0], self.alsensorML[1], 	1, 80)
		
		self.resetearCeldas()
		
	def encenderMotores(self,motor):
		#Metodo que activa los motores
		#Entrada: 	self-> 	Objeto propio de python
		#			motor->	Selector del motor: 
		#					Con: Concentrado, Min: mineral Lev: levadura
		
		if (motor=='Con'):
			if self.aConObj!=0:
				#Encendido motor Con
				velocidad = 99#self.aConCrucero
				self.amConPWM.ChangeDutyCycle(velocidad)
				GPIO.output(self.amCon[0],1)
			else:
				print("Masa es 0, concentrado no encendido")
			return
			
		if (motor=='Min'):
			if self.aMinObj!=0:
				self.amMinPWM.ChangeFrequency(750)
				self.amMinPWM.ChangeDutyCycle(50)
			else:
				print("Masa igual a 0, mineral no encendido")
			return
			
		if (motor=='Lev'):
			if self.aLevObj!=0:
				self.amLevPWM.ChangeFrequency(750)
				self.amLevPWM.ChangeDutyCycle(50)
			else:
				print("Masa igual a 0, levadura no encendido")
			return
			
		else:
			print("Motor no encontrado")
		
	def desacelerarMotores(self,motor):
		#Metodo que desacelera los motores
		if(motor=='Con'):
			velocidad = self.aConMin
			self.amConPWM.ChangeDutyCycle(velocidad)
			return
			
		if(motor=='Min'):
			self.amMinPWM.ChangeFrequency(200)
			self.amMinPWM.ChangeDutyCycle(50)
			return
			
		if(motor=='Lev'):
			self.amLevPWM.ChangeFrequency(200)
			self.amLevPWM.ChangeDutyCycle(50)
			return
			
		else:
			print("Motor no encontrado")
			return
			
	def apagarMotores(self,motor,condicion):
		#Detener motores
		#Entradas: motor: 	Seleccion del motor deseado
		#					Con -> Concentrado
		#					Min -> Mineral
		#					Lev -> Levadura
		#		Condicion:	Indica si el motor no fue apagado en la iteracion anterior
		if (motor=='Con'):
			GPIO.output(self.amCon[0],0)
			self.amConPWM.stop()
			if condicion:
				print("Concentrado apagado")
			return
			
		if (motor=='Min'):
			self.amMinPWM.ChangeFrequency(50)
			self.amMinPWM.ChangeDutyCycle(0)
			if condicion:
				print("Mineral apagado")
			return
			
		if (motor=='Lev'):
			self.amLevPWM.ChangeFrequency(50)
			self.amLevPWM.ChangeDutyCycle(0)
			if condicion:
				print("Levadura apagado")
			return
			
		else:
			print("Motor no encontrado")
			return
	
	def abrirCerrarValvulas(self,valvula,condicion):
		#Metodo de abrir y cerrar valvulas
		#Entradas:	valvula: 
		#				Tolv -> Puerta de la tolva Romana
		#				Min	->	Compuerta del mineral
		#				Lev ->	Compuerta levadura
		#			condicion:
		#				0 	->	Valvula cerrada
		#				1	->	Valvula abierta
		
		if (valvula=='Tolv'):
			GPIO.output(self.avTolva,condicion)
			return
		
		if (valvula =='Min'):
			GPIO.output(self.avMineral,condicion)
			return
		
		if (valvula =='Lev'):
			GPIO.output(self.avLevadura,condicion)
			return
			
		else:
			print("Valvula incorrecta")
	
	def cambiarSensibilidad(self,celda,sensibilidad):
	#Metodo para cambiar la sensibilidad de la celda de carga: (depuracion) 
		#Formato de celda: 'Min','Lev','A','B'
		#Entradas: celda: A1, A2, B1, B2, Min, Lev
		print("Cambiando sensibilidad")
		if (celda=='A1'):
			self.asA1 = sensibilidad
			self.axA.select_channel(channel='A')
			self.axA.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='A2'):
			self.asA2 = sensibilidad	
			self.axA.select_channel(channel='B')
			self.axA.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='B1'):
			self.asB1 = sensibilidad
			self.axB.select_channel(channel='A')
			self.axB.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='B2'):
			self.asB2 = sensibilidad
			self.axB.select_channel(channel='B')
			self.axB.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='Min'):
			self.asMin = sensibilidad
			self.axML.select_channel(channel='A')
			self.axML.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='Lev'):
			self.asLev = sensibilidad
			self.axML.select_channel(channel='A')
			self.axML.set_scale_ratio(sensibilidad)
			return
			
		else:
			print("Celda no encontrada")
			
	def leerMineral(self,lecturas):
	#Leer el peso del mineral en gramos.
		#Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
		
		#Mineral puerto A del sensor
		masaMin = -((self.ahxML.weighOnce())-self.asZeroMin)/self.asMin
		return masaMin
	
	def leerLevadura(self,lecturas):
	#Leer el peso del mineral en gramos.
		#Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
		masaLev = (self.ahxML.weighOnce()-self.asZeroLev)/self.asLev
		return masaLev
	
	def leerConcentrado(self,lecturas):
	#Leer el peso del concentrado en gramos.
		#Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
		
		Conc1 	= self.ahxC1.weighOnce()-self.asZeroC1
		
		Conc2 	= self.ahxC2.weighOnce()-self.asZeroC2
		
		Conc3 	= self.ahxC3.weighOnce()-self.asZeroC3
				
		Conc4	= -(self.ahxC4.weighOnce()-self.asZeroC4)
				
		Conc = (Conc1+Conc2+Conc3+Conc4)/(4*self.asConc)
		#Nota: De momento se estan leyendo solo las celdas de los puertos A del concentrado
		#		las celdas B presetan problemas de retardos en las lecturas
		return float(Conc)
	
	def cerrarSteppers(self):
	#Metodo para apagar puertos de velocidad de los motores 
			self.amMinPWM.stop()
			self.amLevPWM.stop()
			self.amConPWM.stop()
			
	def leer4Concentrado(self):
	#Metodo para leer por separado cada celda de carga del concentrado 	(depuracion)
		Conc1 	= self.ahxC1.weighOnce()-self.asZeroC1
		
		Conc2 	= self.ahxC2.weighOnce()-self.asZeroC2
		
		Conc3 	= self.ahxC3.weighOnce()-self.asZeroC3
				
		Conc4	= -(self.ahxC4.weighOnce()-self.asZeroC4)
		print("%d\t%d\t%d\t%d"%(Conc1,Conc2,Conc3,Conc4))
		
	def leer4ConcentradoRaw(self,lecturas):
	#Metodo para leer cada celda del concentrado sin restar tara 		(depuracion)
		Conc1 	= self.ahxC1.weighOnce()
		
		Conc2 	= self.ahxC2.weighOnce()
		
		Conc3 	= self.ahxC3.weighOnce()
				
		Conc4	= self.ahxC4.weighOnce()
				
		print("%d\t%d\t%d\t%d"%(Conc1,Conc2,Conc3,Conc4))
		
	def tararConcentrado(self,imprimir= False,lecturas=30):
		#Metodo para tarar los amplificadores del concentrado
		if imprimir:
			print("Tarando")
		
		self.asZeroC1 	= self.ahxC1.weigh(80)
		 
		self.asZeroC2 	= self.ahxC2.weigh(80)
		
		self.asZeroC3 	= self.ahxC3.weigh(80)
		
		self.asZeroC4 	= self.ahxC4.weigh(80)
		
		if imprimir:
			print("Tara del concentrado\n%d\t%d\t%d\t%d\t"%
			(self.asZeroC1,self.asZeroC2,self.asZeroC3,self.asZeroC4))

	def tararMineral(self,printVal=False,lecturas=30):
	#Metodo para tarar mineral
	
		self.asZeroMin = self.ahxML.tare(lecturas,False)
		if printVal:
			print("\tTara del mineral %d"%(self.asZeroMin))
	
	def tararLevadura(self,printVal=False,lecturas = 30):
	#Metodo para tarar levdura
		self.asZeroMin = self.ahxML.tare(lecturas,False)
		if printVal:
			print("\tTara de la levadura %d"%(self.asZeroMin))

	def filtradorTamizador(self,dato,alimento):
	#Metodo para filtrar y tamizar los valores de las celdas de carga
	#Se aplica un filtro de media movil con tres periodos,
	#luego se eliminan las lecturas que presenten cambios abruptos respecto de los valores predecesores.
		if alimento == 'Con':
			
			#Tamizar
			if ((abs(dato-self.aDato_k_1[0]))>self.razon[0]):
				datoT = self.aX_k_1[0]
				#print("Tamizado")
			else:
				datoT = dato
			
			#Filtrar
			self.aSk[0] 		= self.aSk[0]-self.aPeso_kbuffer[0][self.aContador[0]]+datoT
			concentrado 		= self.aSk[0]/5
			self.aPeso_kbuffer[0][self.aContador[0]] = datoT
			
			#Mover el contador y retrasar las muestras
			self.aContador[0] 	+=	1
			self.aDato_k_1[0]	= dato
			self.aX_k_1[0]		= datoT
			
			if self.aContador[0] 	== 5:
				self.aContador[0] 	= 0
			return concentrado
			
		if alimento == 'Min':
			#Tamizar
			if ((abs(dato-self.peso_k_1[1]))>self.razon[1]):
				dato = self.peso_k_1[1]
				print("Tamizado")
			#Filtrar
			mineral			 	= (dato+self.peso_k_1[1]+self.peso_k_2[1])/3
			self.peso_k_2[1]	= self.peso_k_1[1]
			self.peso_k_1[1]	= mineral
			return mineral
		
		if alimento == 'Lev':
			#Tamizar
			if ((abs(dato-self.peso_k_1[2]))>self.razon[2]):
				dato = self.peso_k_1[2]
				print("Tamizado")
			#Filtrar
			levadura 			= (dato+self.peso_k_1[2]+self.peso_k_2[2])/3
			self.peso_k_2[2]	= self.peso_k_1[2]
			self.peso_k_1[2]	= levadura
			return levadura
			
		else:
			print("Alimento no encontrado")
			
	def	inRangeCoerce(self,dato, minimo = 0.0, maximo = 100.0):
	#Metodo que limita los valores de una variable
		if dato > maximo:
			return maximo
			
		if dato < minimo:
			return minimo
		
		else:
			return dato
			
	def normalizarVelocidadConcentrado(self,dato):
	#Metodo para normalizar los valores del concentrado
		#Debido a la electronica, el valor de PWM permitido es entre 39 y 99.
		#Fuera de esos valores comienza a presentarse comportamiento erratico.
		dato = self.inRangeCoerce(dato,0,100)
		dato = (self.maxCon-self.minCon)/100 * dato + self.minCon
		return dato
	
	#Metodos para resumir bloques de la secuencia
	def tararCeldas(self):
	#Metodo para tarar todas las cedas de carga. Permite no hacerlo desde el main
		self.leerMineral(80)
		print("________________\nTarando Concentrado\n________________\n")
		self.tararConcentrado(80,True)
		print("Zero A1 ",self.asZeroC1)
		print("Zero A2 ",self.asZeroC2)
		print("Zero B1 ",self.asZeroC3)
		print("Zero B2 ",self.asZeroC4)
		
		print("________________\nTarando Mineral\n________________\n")
		self.tararMineral(80)
		print("________________\nTarando Levadura\n________________\n")
		self.tararLevadura(80)

	def resetearCeldas(self):
		print("Reseteando celdas de carga concentrado")
		#Celdas del concentrado
		self.ahxC1.turnOff()
		time.sleep(0.5)
		self.ahxC1.turnOn()
		time.sleep(0.5)
		
		self.ahxC1.turnOff()
		time.sleep(0.5)
		self.ahxC1.turnOn()
		time.sleep(0.5)
		
		self.ahxC2.turnOff()
		time.sleep(0.5)
		self.ahxC2.turnOn()
		time.sleep(0.5)
		
		self.ahxC3.turnOff()
		time.sleep(0.5)
		self.ahxC3.turnOn()
		time.sleep(0.5)
		
		self.ahxC4.turnOff()
		time.sleep(0.5)
		self.ahxC4.turnOn()
		time.sleep(0.5)		
		
		print("Reseteando celdas de carga Mineral y Levadura")
		self.ahxML.turnOff()
		time.sleep(0.5)
		self.ahxML.turnOn()
		time.sleep(0.5)
		
	def filtroButterWorth(self,xk):
		self.yk = (0.7769*self.xk_1 #- 0.007079*self.xk_2
			 + 0.2231*self.yk_1) #- 0.000002 * self.yk_2)
		#Retrasar muestras
		#self.xk_4	= self.xk_3
		#self.xk_3	= self.xk_2
		self.xk_2	= self.xk_1	  
		self.xk_1	= xk	
		
		#self.yk_4	= self.yk_3
		#self.yk_3	= self.yk_2
		self.yk_2	= self.yk_1	  
		self.yk_1	= self.yk
		return self.yk	
		
	def controlPI(self,xk):
		if ((xk +250) < self.aConObj):
			ek 	= self.aConObj-xk
			PI	= ek*self.kp+self.ki*self.ik
			PIl 	= self.inRangeCoerce(PI,0,99)
			self.amConPWM.ChangeDutyCycle(PIl)
			self.ik	= ek*0.1+self.ik
			return PI
		else:
			self.apagarMotores("Con",False)	
		#Anti Windup
		self.inRangeCoerce(self.ik,-100/self.ki,100/self.ki)
Exemplo n.º 4
0
class Dosificadora:
    def __init__(self):
        #Crear el objeto de la clase dosificadora

        ##Convenciones: axxxx: a significa atributo
        #Con-> Concentrado
        #Min-> Mineral
        #Lev-> Levadura

        #Puertos de control para las valvulas
        self.avTolva = 2
        self.avMineral = 16
        self.avLevadura = 27

        #Puertos de asignacion de las celdas de carga
        self.alsensorC1 = (11, 9)  #Formato tupla: (dt,sck)
        self.alsensorC2 = (22, 10)
        self.alsensorC3 = (24, 23)
        self.alsensorC4 = (12, 6)
        self.alsensorML = (19, 13)

        #Puertos control de motores
        self.amCon = (7, 8)  #Formato tupla: (Encendido, velocidad)
        self.amMin = (20, 21)  #Formato tupla: (velocidad, sentido)
        self.amLev = (25, 26)

        #Sensibilidades celdas de carga
        self.asMin = 1030.3320
        self.asLev = 2563.3821
        self.asC1 = 1
        self.asC2 = 1
        self.asC3 = 1
        self.asC4 = 1
        self.asConc = 53.2201

        #Valores de Tara para cada celda de carga
        self.asZeroMin = 0
        self.asZeroLev = 0
        self.asZeroC1 = 0
        self.asZeroC2 = 0
        self.asZeroC3 = 0
        self.asZeroC4 = 0

        #Masas objetivo
        self.aConObj = 1
        self.aMinObj = 1
        self.aLevObj = 1
        self.aMasaObj = [self.aConObj, self.aMinObj, self.aLevObj]

        #Parametros del filtro tamizador y media movil
        self.aPeso_kbuffer = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
                              [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
                              [0.0, 0.0, 0.0, 0.0, 0.0,
                               0.0]]  #Formato lista [Con,Min,Lev]
        self.aSk = [0.0, 0.0, 0.0]  #Formato listas [Con,Min,Lev]
        self.aContador = [0, 0, 0]
        self.aDato_k_1 = [0.0, 0.0, 0.0]
        self.aX_k_1 = [0.0, 0.0, 0.0]
        self.aPeso_kbuffer = np.zeros((3, 20), dtype=np.float32)

        #Valores para algoritmo de control
        self.aMultiplo = [0.8, 0.8, 0.8]  #Formato listas [Con,Min,Lev]
        self.aDeltaRef = [0.0, 0.0, 0.0]

        self.aInt = [0.0, 0.0, 0.0]
        self.aKp = [0.00051743, 0.0051743, 0.0]
        self.aKi = [0.0, 0.0, 0.0]
        self.aKd = [0.00080848, 0.0080848, 0.0]
        self.aN = [0.3704, 0.0, 0.3704]

        self.aVk_1 = 0.0
        self.aEk_1 = 0.0
        self.aYk_1 = 0.0
        self.aDk_1 = 0.0
        self.aUk_1 = 0
        self.aRk_1 = 0

        self.aInt_Retardo = 0
        self.aTcontador = 0
        self.aDoCalcularKp = [True, True, True]
        self.aPWM = [0.0, 0.0, 0.0]

        self.aAceleracion = [300.0, 0.0, 0.0]
        #Otros atributos
        self.asText = "________________"  #Separador de Textos
        self.minCon = 39.0  #Menor ciclo de PWM permitido para el concentrado
        self.maxCon = 99.0  #Mayor ciclo de PWM permitido para el concentrado
        self.razon = [
            60.0, 50.0, 10.0
        ]  #Mayor tasa de cambio permitida por el filtro tamizador
        #Formato lista [Con,Min,Lev]
        self.aConCrucero = 70.0  #Velocidad crucero motor Con
        self.aConMin = 60.0  #Minima velocidad para mover el motor
        self.aEspacio = "_________________"

    def __del__(self):
        #Metodo destructor de objeto
        nombre = self.__class__.__name__
        print(nombre, "Destruido")

    def inicializarPuertos(self):
        #Encargado de iniciar el estado de los puertos de RPi.

        print("\n________________\nIniciando puertos\n________________\n")
        #Configurar puertos
        #Valvulas
        GPIO.setup(self.avTolva, GPIO.OUT)
        GPIO.setup(self.avMineral, GPIO.OUT)
        GPIO.setup(self.avLevadura, GPIO.OUT)

        #Motores
        #Concentrado
        GPIO.setup(self.amCon[0], GPIO.OUT)
        GPIO.setup(self.amCon[1], GPIO.OUT)

        #Mineral
        GPIO.setup(self.amMin[0], GPIO.OUT)
        GPIO.setup(self.amLev[0], GPIO.OUT)

        #Levadura
        GPIO.setup(self.amMin[1], GPIO.OUT)
        GPIO.setup(self.amLev[1], GPIO.OUT)

        #Colocar todos los puertos en BAJO "LOW".
        GPIO.output(self.avTolva, 0)
        GPIO.output(self.avMineral, 0)
        GPIO.output(self.avLevadura, 0)

        GPIO.output(self.amCon[0], 0)
        GPIO.output(self.amCon[1], 0)

        GPIO.output(self.amMin[0], 0)
        GPIO.output(self.amMin[1], 0)

        GPIO.output(self.amLev[0], 0)
        GPIO.output(self.amLev[1], 0)

    def inicializarMotores(self):
        #Iniciar el estado de los motores
        #Frecuencia de PWM
        self.amMinPWM = GPIO.PWM(self.amMin[0],
                                 300)  #Formato tupla: (velocidad, sentido)
        self.amLevPWM = GPIO.PWM(self.amLev[0],
                                 300)  #Formato tupla: (velocidad, sentido)
        self.amConPWM = GPIO.PWM(self.amCon[1], 250)

        ##Iniciar PWM en valor 0
        self.amMinPWM.start(0)
        self.amLevPWM.start(0)
        self.amConPWM.start(0)

    def inicializarCeldas(self):
        #Inciar celdas de carga
        print(
            "\n________________\nIniciando celdas de carga\n________________\n"
        )
        #Formato tupla: self.alsensorA	=	(dt,sck)

        #Celda de carga Concentrado C1
        self.ahxC1 = Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
        #Celda de carga Concentrado C2
        self.ahxC2 = Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
        #Celda de carga Concentrado C3
        self.ahxC3 = Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
        #Celda de carga Concentrado C4
        self.ahxC4 = Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)
        #Celda de carga Levadura Mineral
        self.ahxML = Scale(self.alsensorML[0], self.alsensorML[1], 1, 80)

        self.resetearCeldas()

    def encenderMotores(self, motor):
        #Metodo que activa los motores
        #Entrada: 	self-> 	Objeto propio de python
        #			motor->	Selector del motor:
        #					Con: Concentrado, Min: mineral Lev: levadura

        if (motor == 'Con'):
            if self.aConObj != 0:
                #Encendido motor Con
                velocidad = 99  #self.aConCrucero
                self.amConPWM.ChangeDutyCycle(velocidad)
                GPIO.output(self.amCon[0], 1)
            else:
                print("Masa es 0, concentrado no encendido")
            return

        if (motor == 'Min'):
            if self.aMinObj != 0:
                self.amMinPWM.ChangeFrequency(750)
                self.amMinPWM.ChangeDutyCycle(50)
            else:
                print("Masa igual a 0, mineral no encendido")
            return

        if (motor == 'Lev'):
            if self.aLevObj != 0:
                self.amLevPWM.ChangeFrequency(750)
                self.amLevPWM.ChangeDutyCycle(50)
            else:
                print("Masa igual a 0, levadura no encendido")
            return

        else:
            print("Motor no encontrado")

    def desacelerarMotores(self, motor):
        #Metodo que desacelera los motores
        if (motor == 'Con'):
            velocidad = self.aConMin
            self.amConPWM.ChangeDutyCycle(velocidad)
            return

        if (motor == 'Min'):
            self.amMinPWM.ChangeFrequency(200)
            self.amMinPWM.ChangeDutyCycle(50)
            return

        if (motor == 'Lev'):
            self.amLevPWM.ChangeFrequency(200)
            self.amLevPWM.ChangeDutyCycle(50)
            return

        else:
            print("Motor no encontrado")
            return

    def apagarMotores(self, motor, condicion):
        #Detener motores
        #Entradas: motor: 	Seleccion del motor deseado
        #					Con -> Concentrado
        #					Min -> Mineral
        #					Lev -> Levadura
        #		Condicion:	Indica si el motor no fue apagado en la iteracion anterior
        if (motor == 'Con'):
            GPIO.output(self.amCon[0], 0)
            self.amConPWM.stop()
            if condicion:
                print("Concentrado apagado")
            return

        if (motor == 'Min'):
            self.amMinPWM.ChangeFrequency(50)
            self.amMinPWM.ChangeDutyCycle(0)
            if condicion:
                print("Mineral apagado")
            return

        if (motor == 'Lev'):
            self.amLevPWM.ChangeFrequency(50)
            self.amLevPWM.ChangeDutyCycle(0)
            if condicion:
                print("Levadura apagado")
            return

        else:
            print("Motor no encontrado")
            return

    def abrirCerrarValvulas(self, valvula, condicion):
        #Metodo de abrir y cerrar valvulas
        #Entradas:	valvula:
        #				Tolv -> Puerta de la tolva Romana
        #				Min	->	Compuerta del mineral
        #				Lev ->	Compuerta levadura
        #			condicion:
        #				0 	->	Valvula cerrada
        #				1	->	Valvula abierta

        if (valvula == 'Tolv'):
            GPIO.output(self.avTolva, condicion)
            return

        if (valvula == 'Min'):
            GPIO.output(self.avMineral, condicion)
            return

        if (valvula == 'Lev'):
            GPIO.output(self.avLevadura, condicion)
            return

        else:
            print("Valvula incorrecta")

    def cambiarSensibilidad(self, celda, sensibilidad):
        #Metodo para cambiar la sensibilidad de la celda de carga: (depuracion)
        #Formato de celda: 'Min','Lev','A','B'
        #Entradas: celda: A1, A2, B1, B2, Min, Lev
        print("Cambiando sensibilidad")
        if (celda == 'A1'):
            self.asA1 = sensibilidad
            self.axA.select_channel(channel='A')
            self.axA.set_scale_ratio(sensibilidad)
            return

        if (celda == 'A2'):
            self.asA2 = sensibilidad
            self.axA.select_channel(channel='B')
            self.axA.set_scale_ratio(sensibilidad)
            return

        if (celda == 'B1'):
            self.asB1 = sensibilidad
            self.axB.select_channel(channel='A')
            self.axB.set_scale_ratio(sensibilidad)
            return

        if (celda == 'B2'):
            self.asB2 = sensibilidad
            self.axB.select_channel(channel='B')
            self.axB.set_scale_ratio(sensibilidad)
            return

        if (celda == 'Min'):
            self.asMin = sensibilidad
            self.axML.select_channel(channel='A')
            self.axML.set_scale_ratio(sensibilidad)
            return

        if (celda == 'Lev'):
            self.asLev = sensibilidad
            self.axML.select_channel(channel='A')
            self.axML.set_scale_ratio(sensibilidad)
            return

        else:
            print("Celda no encontrada")

    def leerMineral(self, lecturas):
        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        mineral = 0.0
        for i in range(lecturas):
            masaMin = -(
                (self.ahxML.weighOnce()) - self.asZeroMin)  #/self.asMin
            mineral += masaMin
        mineral / lecturas
        return mineral

    def leerLevadura(self, lecturas):
        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        masaLev = (self.ahxML.weighOnce() - self.asZeroLev) / self.asLev
        return masaLev

    def leerConcentrado(self, lecturas):
        #Leer el peso del concentrado en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de promediar y devolver

        concentrado = 0.0
        for i in range(lecturas):
            Conc1 = self.ahxC1.weighOnce() - self.asZeroC1
            Conc2 = self.ahxC2.weighOnce() - self.asZeroC2
            Conc3 = self.ahxC3.weighOnce() - self.asZeroC3
            Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)

            Conc = (Conc1 + Conc2 + Conc3 + Conc4) / (4 * self.asConc)
            concentrado += Conc
        concentrado = concentrado / lecturas
        return float(concentrado)

    def cerrarSteppers(self):
        #Metodo para apagar puertos de velocidad de los motores
        self.amMinPWM.stop()
        self.amLevPWM.stop()
        self.amConPWM.stop()

    def leer4Concentrado(self):
        #Metodo para leer por separado cada celda de carga del concentrado 	(depuracion)
        Conc1 = self.ahxC1.weighOnce() - self.asZeroC1

        Conc2 = self.ahxC2.weighOnce() - self.asZeroC2

        Conc3 = self.ahxC3.weighOnce() - self.asZeroC3

        Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)
        print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4))

    def leer4ConcentradoRaw(self, lecturas):
        #Metodo para leer cada celda del concentrado sin restar tara 		(depuracion)
        Conc1 = self.ahxC1.weighOnce()

        Conc2 = self.ahxC2.weighOnce()

        Conc3 = self.ahxC3.weighOnce()

        Conc4 = self.ahxC4.weighOnce()

        print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4))

    def tararConcentrado(self, imprimir=False, lecturas=30):
        #Metodo para tarar los amplificadores del concentrado
        if imprimir:
            print("Tarando")

        self.asZeroC1 = self.ahxC1.weigh(lecturas)

        self.asZeroC2 = self.ahxC2.weigh(lecturas)

        self.asZeroC3 = self.ahxC3.weigh(lecturas)

        self.asZeroC4 = self.ahxC4.weigh(lecturas)

        if imprimir:
            print("Tara del concentrado\n%d\t%d\t%d\t%d\t" %
                  (self.asZeroC1, self.asZeroC2, self.asZeroC3, self.asZeroC4))

    def tararMineral(self, printVal=False, lecturas=30):
        #Metodo para tarar mineral
        self.asZeroMin = self.ahxML.weigh(lecturas)
        if printVal:
            print("\tTara del mineral %d" % (self.asZeroMin))

    def tararLevadura(self, printVal=False, lecturas=30):
        #Metodo para tarar levdura
        self.asZeroMin = self.ahxML.weigh(lecturas)
        if printVal:
            print("\tTara de la levadura %d" % (self.asZeroMin))

    def filtradorTamizador(self, dato, alimento, periodos=5):
        #Metodo para filtrar y tamizar los valores de las celdas de carga
        #Se aplica un filtro de media movil con tres periodos,
        #luego se eliminan las lecturas que presenten cambios abruptos respecto de los valores predecesores.
        if alimento == 'Con':

            #Tamizar
            if ((abs(dato - self.aDato_k_1[0])) > self.razon[0]):
                datoT = self.aX_k_1[0]

            else:
                datoT = dato

            #Filtrar
            self.aSk[0] = self.aSk[0] - self.aPeso_kbuffer[0][
                self.aContador[0]] + datoT
            concentrado = self.aSk[0] / periodos
            self.aPeso_kbuffer[0][self.aContador[0]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[0] += 1
            self.aDato_k_1[0] = dato
            self.aX_k_1[0] = datoT

            if self.aContador[0] == periodos:
                self.aContador[0] = 0
            return concentrado

        if alimento == 'Min':
            #Tamizar
            if ((abs(dato - self.aDato_k_1[1])) > self.razon[1]):
                datoT = self.aX_k_1[1]

            else:
                datoT = dato

            #Filtrar
            self.aSk[1] = self.aSk[1] - self.aPeso_kbuffer[1][
                self.aContador[1]] + datoT
            mineral = self.aSk[1] / periodos
            self.aPeso_kbuffer[1][self.aContador[1]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[1] += 1
            self.aDato_k_1[1] = dato
            self.aX_k_1[1] = datoT

            if self.aContador[1] == periodos:
                self.aContador[1] = 0
            return mineral

        if alimento == 'Lev':
            #Tamizar
            if ((abs(dato - self.aDato_k_1[2])) > self.razon[2]):
                datoT = self.aX_k_1[2]

            else:
                datoT = dato

            #Filtrar
            self.aSk[2] = self.aSk[2] - self.aPeso_kbuffer[2][
                self.aContador[2]] + datoT
            levadura = self.aSk[2] / periodos
            self.aPeso_kbuffer[2][self.aContador[2]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[2] += 1
            self.aDato_k_1[2] = dato
            self.aX_k_1[2] = datoT

            if self.aContador[2] == periodos:
                self.aContador[2] = 0
            return levadura

        else:
            print("Alimento no encontrado")

    def inRangeCoerce(self, dato, minimo=0.0, maximo=100.0):
        #Metodo que limita los valores de una variable
        if dato > maximo:
            return maximo

        if dato < minimo:
            return minimo

        else:
            return dato

    def normalizarVelocidadConcentrado(self, dato):
        #Metodo para normalizar los valores del concentrado
        #Debido a la electronica, el valor de PWM permitido es entre 39 y 99.
        #Fuera de esos valores comienza a presentarse comportamiento erratico.
        dato = self.inRangeCoerce(dato, 0, 100)
        dato = (self.maxCon - self.minCon) / 100 * dato + self.minCon
        return dato

    #Metodos para resumir bloques de la secuencia
    def tararCeldas(self):
        #Metodo para tarar todas las cedas de carga. Permite no hacerlo desde el main
        self.leerMineral(80)
        print("________________\nTarando Concentrado\n________________\n")
        self.tararConcentrado(80, True)
        print("Zero A1 ", self.asZeroC1)
        print("Zero A2 ", self.asZeroC2)
        print("Zero B1 ", self.asZeroC3)
        print("Zero B2 ", self.asZeroC4)

        print("________________\nTarando Mineral\n________________\n")
        self.tararMineral(80)
        print("________________\nTarando Levadura\n________________\n")
        self.tararLevadura(80)

    def resetearCeldas(self):
        print("Reseteando celdas de carga concentrado")
        #Celdas del concentrado
        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC2.turnOff()
        time.sleep(0.5)
        self.ahxC2.turnOn()
        time.sleep(0.5)

        self.ahxC3.turnOff()
        time.sleep(0.5)
        self.ahxC3.turnOn()
        time.sleep(0.5)

        self.ahxC4.turnOff()
        time.sleep(0.5)
        self.ahxC4.turnOn()
        time.sleep(0.5)

        print("Reseteando celdas de carga Mineral y Levadura")
        self.ahxML.turnOff()
        time.sleep(0.5)
        self.ahxML.turnOn()
        time.sleep(0.5)

    def filtroButterWorth(self, xk):
        self.yk = (
            0.7769 * self.xk_1  #- 0.007079*self.xk_2
            + 0.2231 * self.yk_1)  #- 0.000002 * self.yk_2)
        #Retrasar muestras
        #self.xk_4	= self.xk_3
        #self.xk_3	= self.xk_2
        self.xk_2 = self.xk_1
        self.xk_1 = xk

        #self.yk_4	= self.yk_3
        #self.yk_3	= self.yk_2
        self.yk_2 = self.yk_1
        self.yk_1 = self.yk
        return self.yk

    def controlPDCon(self, yk):
        #Comienza algoritmo de control
        #Calculo del error
        ek = (self.aMasaObj[0] - yk)
        #Estimacion del control PD
        pk = self.aKp[0] * (ek)
        dk = ((self.aKd[0] * self.aN[0]) *
              (ek - self.aEk_1) + self.aDk_1) / (1 + self.aN[0] * 0.05)
        pidk = pk + dk + 0.3
        #Compensacion de componente no lineal
        pidk = 100 * self.inRangeCoerce(pidk, 0, 0.99)
        self.amConPWM.ChangeDutyCycle(pidk)
        #Termina algoritmo de control

        #Retrasa las muestras
        self.aYk_1 = yk
        self.aDk_1 = dk
        self.aEk_1 = ek
        self.aPWM[0] = pidk

    def controlPDLev(self, yk):
        #Comienza algoritmo de control
        #Calculo del error
        ek = (self.aMasaObj[2] - yk)
        #Estimacion del control PD
        pk = self.aKp[2] * (ek)
        dk = ((self.aKd[2] * self.aN[2]) *
              (ek - self.aEk_1) + self.aDk_1) / (1 + self.aN[2] * 0.05)
        pidk = pk + dk + 0.4
        #Compensacion de componente no lineal
        pidk = self.inRangeCoerce(pidk, 0, 0.99)
        pidk = self.escalar(pidk, 0, 1400)
        self.amLevPWM.ChangeFrequency(pidk)

        #Termina algoritmo de control

        #Retrasa las muestras
        self.aYk_1_Lev = yk
        self.aDk_1_Lev = dk
        self.aEk_1_Lev = ek
        self.aPWM[2] = pidk

    def escalar(self, dato, outMin, outMax, inMin=0, inMax=1):
        m = (outMin - outMax) / (inMin - inMax)
        b = outMin - inMin * m
        dato = dato * m + b
        return dato

    ##-----	Metodos para pruebas por separado ----- ##

    def InicializarCeldasTR(self):
        #Inicializa solamente las celdas de carga de la tolva romana
        self.ahxC1 = Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
        #Celda de carga Concentrado C2
        self.ahxC2 = Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
        #Celda de carga Concentrado C3
        self.ahxC3 = Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
        #Celda de carga Concentrado C4
        self.ahxC4 = Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)

    def InicializarCeldasML(self):
        #Inicializa las celdas de carga del mineral/Levadura
        self.ahxML = Scale(self.alsensorML[0], self.alsensorML[1], 1, 80)

    def ResetearCeldasTR(self):
        #Resetea las celdas de carga del concentrado
        print("Reseteando celdas de carga concentrado")

        self.ahxC1.turnOff()
        self.ahxC2.turnOff()
        self.ahxC3.turnOff()
        self.ahxC4.turnOff()
        time.sleep(0.5)

        self.ahxC1.turnOn()
        self.ahxC2.turnOn()
        self.ahxC3.turnOn()
        self.ahxC4.turnOff()
        time.sleep(0.5)

    def resetearCeldasML(self):
        #Resetea las celdas de carga del Mineral/Levadura
        print("Reseteando celdas de carga Mineral-Levadura")
        #Celdas del Mineral/Levadura
        self.ahxML.turnOff()
        time.sleep(0.5)
        self.ahxML.turnOn()

    ## --- Metodos para pruebas individuales --------
    def probarCeldasMinLev(self, tiempo):
        #Obtiene muestras de la celda de carga por una cantidad determinada de tiempo
        print(self.aEspacio)
        print("Probando celdas de carga Mineral-Levadura")
        print(self.aEspacio)

        self.InicializarCeldasML()
        self.resetearCeldasML()
        self.tararMineral(False, 80)
        self.tararLevadura(False, 80)

        print("Sin filtro\tCon Filtro")
        tic = time.time()
        while True:
            Min = self.leerMineral(80)

            #Calcular filtro de media movil en linea
            MinF = self.filtradorTamizador(Min, 'Min', 5)
            print("%f\t%f" % (Min, MinF))
            toc = time.time()

            #Condicion de parada para el ciclo
            if ((toc - tic) >= tiempo):
                break

    def probarCeldasTR(self, tiempo):
        #Obtiene muestras de la celda de carga por una cantidad determinada de tiempo
        print(self.aEspacio)
        print("Probando celdas de carga Tolva Romana")
        print(self.aEspacio)

        self.InicializarCeldasTR()
        self.ResetearCeldasTR()
        self.tararConcentrado(False, 80)

        print("Sin filtro\tCon Filtro")
        tic = time.time()
        while True:
            Con = self.leerConcentrado(80)

            #Calcular filtro de media movil en linea
            ConF = self.filtradorTamizador(Con, 'Con', 5)
            print("%f\t%f" % (Con, ConF))
            toc = time.time()

            #Condicion de parada para el ciclo
            if ((toc - tic) >= tiempo):
                break

    def obtenerFuncionRampaTR(self, maxSpeed=99):

        #metodo que se usa para obtener la rampa de velocidad del citrocom
        aceleracion = True
        print(self.aEspacio)
        print("Obteniendo rampa")
        print(self.aEspacio)
        self.inicializarPuertos()
        self.InicializarCeldasTR()
        self.ResetearCeldasTR()
        self.tararConcentrado()

        self.inicializarMotores()
        self.amConPWM.ChangeDutyCycle(99)
        print("Arrancado")
        self.encenderMotores('Con')

        c = 0
        self.amConPWM.start(0)

        while True:

            if aceleracion:
                for i in range(10, maxSpeed, 1):
                    masaConc = self.leerConcentrado(4)
                    masaConcF = self.filtradorTamizador(masaConc, 'Con', 5)
                    self.amConPWM.ChangeDutyCycle(i)
                    time.sleep(0.1)
                    print("%f\t%f\t%f" % (masaConc, masaConcF, i))
                for i in reversed(range(10, maxSpeed, 1)):
                    masaConc = self.leerConcentrado(4)
                    masaConcF = self.filtradorTamizador(masaConc, 'Con', 5)
                    self.amConPWM.ChangeDutyCycle(i)
                    time.sleep(0.1)
                    print("%f\t%f\t%f" % (masaConc, masaConcF, i))
            else:
                if (c == 0):
                    self.amConPWM.ChangeDutyCycle(maxSpeed)
                    c += 0
Exemplo n.º 5
0
def main():
    """
    Read in settings from AMW_config.jsn. If we don't find the file, make it.
    Note that we save the file with new line as a separator, but can't load it with a non-standard separator,
    so we replace new lines with  commas, he default separator character
    
    """
    try:
        with open('AMW_config.jsn', 'r') as fp:
            data = fp.read()
            data = data.replace('\n', ',')
            configDict = json.loads(data)
            fp.close()
            # Constants for saving data
            # cage name, to tell data from different cages
            kCAGE_NAME = configDict.get('Cage Name')
            # path where data from each day will be saved
            kCAGE_PATH = configDict.get('Data Path')
            # rollover time, 0 to start the file for each day at 12 midnight. Could set to 7 to synch files to mouse day/night cycle
            kDAYSTARTHOUR = configDict.get('Day Start Hour')
            # size of array used for threaded reading from load cell amplifier
            kTHREADARRAYSIZE = configDict.get('Thread Array Size')
            # cuttoff weight where we stop the thread from reading when a mouse steps away
            kMINWEIGHT = configDict.get('Minimum Weight')
            # GPIO pin numbers and scaling for HX711, adjust as required for individual setup
            kDATA_PIN = configDict.get('GPIO Data Pin')
            kCLOCK_PIN = configDict.get('GPIO Clock Pin')
            kGRAMS_PER_UNIT = configDict.get('Grams Per Unit')
            # RFID Reader. Note that code as written only works with ID tag readers not RDM readers because of reliance on Tag-In-Range Pin
            kSERIAL_PORT = configDict.get('Serial Port')
            kTIR_PIN = configDict.get('GPIO Tag In Range Pin')
            #whether data is saved locally 1 or, not yet supported, sent to a server 2, or both, 3
            kSAVE_DATA = configDict.get('Data Save Options')
            # a dictionary of ID Tags and cutoff weights, as when monitoring animal weights over time
            kHAS_CUTOFFS = configDict.get('Has Cutoffs')
            if kHAS_CUTOFFS:
                kCUT_OFF_DICT = configDict.get('Cutoff Dict')
            else:
                kCUT_OFF_DICT = None
            # can call get day weights code and email weights, needs extra options
            kEMAIL_WEIGHTS = configDict.get('Email Weights')
            if kEMAIL_WEIGHTS:
                kEMAIL_DICT = configDict.get('Email Dict')
            else:
                kEMAIL_DICT = None
    except (TypeError, IOError, ValueError) as e:
        #we will make a file if we didn't find it, or if it was incomplete
        print(
            'Unable to load configuration data from AMW_config.jsn, let\'s make a new AMW_config.jsn.\n'
        )
        jsonDict = {}
        kCAGE_NAME = input(
            'Enter the cage name, used to distinguish data from different cages:'
        )
        kCAGE_PATH = input(
            'Enter the path where data from each day will be saved:')
        kDAYSTARTHOUR = int(
            input(
                'Enter the rollover hour, in 24 hour format, when a new data file is started:'
            ))
        kTHREADARRAYSIZE = int(
            input(
                'Enter size of array used for threaded reading from Load Cell:'
            ))
        kMINWEIGHT = float(
            input(
                'Enter cutoff weight where we stop the thread from reading:'))
        kDATA_PIN = int(
            input(
                'Enter number of GPIO pin connected to data pin on load cell:')
        )
        kCLOCK_PIN = int(
            input(
                'Enter number of GPIO pin connected to clock pin on load cell:'
            ))
        kGRAMS_PER_UNIT = float(
            input(
                'Enter the scaling of the load cell, in grams per A/D unit:'))
        kSERIAL_PORT = input(
            'Enter the name of serial port used for tag reader,e.g. serial0 or ttyAMA0:'
        )
        kTIR_PIN = int(
            input(
                'Enter number of the GPIO pin connected to the Tag-In-Range pin on the RFID reader:'
            ))
        kSAVE_DATA = int(
            input(
                'To save data locally, enter 1; to send data to a server, not yet supported, enter 2:'
            ))
        tempInput = input('Track weights against existing cutoffs(Y or N):')
        kHAS_CUTOFFS = bool(tempInput[0] == 'y' or tempInput[0] == 'Y')
        if kHAS_CUTOFFS:
            kCUT_OFF_DICT = {}
            while True:
                tempInput = input(
                    'Enter a Tag ID and cuttoff weight, separated by a comma, or return to end entry:'
                )
                if tempInput == "":
                    break
                entryList = tempInput.split(',')
                try:
                    kCUT_OFF_DICT.update({entryList[0]: float(entryList[1])})
                except Exception as e:
                    print('bad data entered', str(e))
        else:
            kCUT_OFF_DICT = None
        jsonDict.update({
            'Has Cutoffs': kHAS_CUTOFFS,
            'Cutoff Dict': kCUT_OFF_DICT
        })
        tempInput = input('Email weights every day ? (Y or N):')
        kEMAIL_WEIGHTS = bool(tempInput[0] == 'y' or tempInput[0] == 'Y')
        if kEMAIL_WEIGHTS:
            kEMAIL_DICT = {}
            kFROMADDRESS = input(
                'Enter the account used to send the email with  weight data:')
            kPASSWORD = input(
                'Enter the password for the email account used to send the mail:'
            )
            kSERVER = input(
                'Enter the name of the email server and port number, e.g., smtp.gmail.com:87, with separating colon:'
            )
            kRECIPIENTS = tuple(
                input(
                    'Enter comma-separated list of email addresses to get the daily weight email:'
                ).split(','))
            kEMAIL_DICT.update({
                'Email From Address': kFROMADDRESS,
                'Email Recipients': kRECIPIENTS
            })
            kEMAIL_DICT.update({
                'Email Password': kPASSWORD,
                'Email Server': kSERVER
            })
        else:
            kEMAIL_DICT = None
        jsonDict.update({
            'Email Weights': kEMAIL_WEIGHTS,
            'Email Dict': kEMAIL_DICT
        })
        # add info to a dictionay we will write to file
        jsonDict.update({
            'Cage Name': kCAGE_NAME,
            'Data Path': kCAGE_PATH,
            'Day Start Hour': kDAYSTARTHOUR,
            'Thread Array Size': kTHREADARRAYSIZE
        })
        jsonDict.update({
            'Minimum Weight': kMINWEIGHT,
            'GPIO Data Pin': kDATA_PIN,
            'GPIO Clock Pin': kCLOCK_PIN
        })
        jsonDict.update({
            'GPIO Tag In Range Pin': kTIR_PIN,
            'Grams Per Unit': kGRAMS_PER_UNIT,
            'Serial Port': kSERIAL_PORT
        })
        jsonDict.update({
            'Data Save Options': kSAVE_DATA,
            'Email Weights': kEMAIL_WEIGHTS
        })
        with open('AMW_config.jsn', 'w') as fp:
            fp.write(
                json.dumps(jsonDict, sort_keys=True, separators=('\r\n', ':')))
    """
    Initialize the scale from variables listed above and do an initial taring
    of the scale with 10 reads. Because pins are only accessed from C++, do not call
    Python GPIO.setup for the dataPin and the clockPin
    """
    scale = Scale(kDATA_PIN, kCLOCK_PIN, kGRAMS_PER_UNIT, kTHREADARRAYSIZE)
    scale.weighOnce()
    scale.tare(10, True)
    """
    Setup tag reader and GPIO for TIR pin, with tagReaderCallback installed as
    an event callback when pin changes either from low-to-high, or from high-to-low.
    """
    tagReader = TagReader('/dev/' + kSERIAL_PORT,
                          doChecksum=False,
                          timeOutSecs=0.05,
                          kind='ID')
    tagReader.installCallBack(kTIR_PIN)
    """
    A new binary data file is opened for each day, with a name containing the 
    current date, so open a file to start with
    """
    now = datetime.fromtimestamp(int(time()))
    startDay = datetime(now.year, now.month, now.day, kDAYSTARTHOUR, 0, 0)
    if startDay > now:  # it's still "yesterday" according to kDAYSTARTHOUR definition of when a day starts
        startDay = startDay - timedelta(hours=24)
    startSecs = startDay.timestamp(
    )  # used to report time of an entry through the weighing tube
    nextDay = startDay + timedelta(hours=24)
    filename = kCAGE_PATH + kCAGE_NAME + '_' + str(
        startDay.year) + '_' + '{:02}'.format(
            startDay.month) + '_' + '{:02}'.format(startDay.day)
    if kSAVE_DATA & kSAVE_DATA_LOCAL:
        print('opening file name = ' + filename)
        outFile = open(filename, 'ab')
        from OneDayWeights import get_day_weights
    """
    Weight data is written to the file as grams, in 32 bit floating point format. Each run of data is
    prefaced by metadata from a 32 bit floating point metaData array of size 2. The first point contains
    the last 6 digits of the RFID code, as a negative value to make it easy for analysis code to find the
    start of each run. The second point contains the time in seconds since the start of the day.
    Both data items have been selected to fit into a 32 bit float.
    """
    metaData = array('f', [0, 0])

    while True:
        try:
            """
            Loop with a brief sleep, waiting for a tag to be read
            or a new day to start, in which case a new data file is made
            """
            while RFIDTagReader.globalTag == 0:
                if datetime.fromtimestamp(int(time())) > nextDay:
                    if kSAVE_DATA & kSAVE_DATA_LOCAL:
                        outFile.close()
                        print('save data date =', startDay.year,
                              startDay.month, startDay.day)
                        try:
                            get_day_weights(kCAGE_PATH, kCAGE_NAME,
                                            startDay.year, startDay.month,
                                            startDay.day, kCAGE_PATH, False,
                                            kEMAIL_DICT, kCUT_OFF_DICT)
                        except Exception as e:
                            print('Error getting weights for today:' + str(e))
                    startDay = nextDay
                    nextDay = startDay + timedelta(hours=24)
                    startSecs = startDay.timestamp()
                    filename = kCAGE_PATH + kCAGE_NAME + '_' + str(
                        startDay.year) + '_' + '{:02}'.format(
                            startDay.month) + '_' + '{:02}'.format(
                                startDay.day)
                    if kSAVE_DATA & kSAVE_DATA_LOCAL:
                        outFile = open(filename, 'ab')
                        print('opening file name = ' + filename)
                else:
                    sleep(kTIMEOUTSECS)
            """
            A Tag has been read. Fill the metaData array and tell the C++ thread to start
            recording weights
            """
            thisTag = RFIDTagReader.globalTag
            startTime = time()
            print('mouse = ', thisTag)
            #scale.turnOn()
            metaData[0] = -(thisTag % 1000000)
            metaData[1] = startTime - startSecs
            scale.threadStart(scale.arraySize)
            nReads = scale.threadCheck()
            lastRead = 0
            """
            Keep reading weights into the array until a new mouse is read by 
            the RFID reader, or the last read weight drops below 2 grams, or
            the array is full, then stop the thread print the metaData array
            and the read weights from the thread array to the file
            """
            while ((RFIDTagReader.globalTag == thisTag or
                    (RFIDTagReader.globalTag == 0
                     and scale.threadArray[nReads - 1] > kMINWEIGHT))
                   and nReads < scale.arraySize):
                if nReads > lastRead:
                    print(nReads, scale.threadArray[nReads - 1])
                    lastRead = nReads
                sleep(0.05)
                nReads = scale.threadCheck()
            nReads = scale.threadStop()
            if kSAVE_DATA & kSAVE_DATA_LOCAL:
                metaData.tofile(outFile)
                scale.threadArray[0:nReads - 1].tofile(outFile)
            if kSAVE_DATA & kSAVE_DATA_REMOTE:
                # modify to send : Time:UNIX time stamp, RFID:FULL RFID Tag, CageID: id, array: weight array
                response = requests.post(
                    kSERVER_URL,
                    data={
                        'tag':
                        thisTag,
                        'cagename':
                        kCAGE_NAME,
                        'datetime':
                        int(startTime),
                        'array':
                        str((metaData +
                             scale.threadArray[0:nReads - 1]).tobytes(),
                            'latin_1')
                    }).text
                if response != '\nSuccess\n':
                    print(reponse)
            #scale.turnOff()
        except KeyboardInterrupt:
            #scale.turnOn()
            event = scale.scaleRunner('10:\tQuit AutoMouseWeight program\n')
            if event == 10:
                if kSAVE_DATA & kSAVE_DATA_LOCAL:
                    outFile.close()
                GPIO.cleanup()
                return
        except Exception as error:
            print("Closing file...")
            outFile.close()
            GPIO.cleanup()
            raise error