FUNCTION "FC2940_ZC_TQ_INDV" : Void
TITLE = FC2940_ZC_TQ_INDV
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : HCR
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion dispositivo tanque.
   VAR_INPUT 
      Pulso1seg : Bool;   // Pulso de 1 segundo
      Arranque : Bool;   // Arranque
      EstadoBocaHombre : Bool;   // Estado boca de hombre
      EstadoNivelBajo : Bool;   // Estado nivel bajo
      EstadoNivelAlto : Bool;   // Estado nivel alto
      EstadoComOK : Bool;   // Estado comunicaciones Ok
      ValorNivelPresion : Real;   // Valor de nivel / presion
      ValorTemperatura : Real;   // Valor temperatura
      HisteresisAltoNivelAgitacion : Real;   // Histeresis superior nivel agitacion
      HisteresisBajoNivelAgitacion : Real;   // Histeresis inferior nivel agitacion
      HisteresisAltoNivelTemp : Real;   // Histeresis superior nivel temperatura
      HisteresisBajoNivelTemp : Real;   // Histeresis inferior nivel temperatura
      TiempoTanqueVacio : Int;   // Tiempo retardo tanque vacio
      TiempoTanqueLLeno : Int;   // Tiempo retardo tanque lleno
      HabilitarFrecAgitacion : Bool;   // Habilitar Frecuencia en Agitacion
      NumeroTanque : Int;   // Numero de tanque
   END_VAR

   VAR_TEMP 
      t_RadioCilindro : Real;
      t_RadioConoInf : Real;
      t_RadioActual : Real;
      t_Vol1 : Real;
      t_Vol2 : Real;
      t_Vol3 : Real;
      t_ConsignaAgitador : Real;
      t_VacioLL : Bool;
      t_VacioEA : Bool;
      t_HistPositiva : Real;
      t_HistNegativa : Real;
      t_LLenoHL : Bool;
      t_LlenoEA : Bool;
      t_x0 : Real;
      t_x1 : Real;
      t_x : Real;
      t_y0 : Real;
      t_y1 : Real;
   END_VAR

   VAR CONSTANT 
      CONSTANTE_EMPIRICA : Real := 10.1;
      CONSTANTE_PI : Real := 3.141593;
      ESTADO_DESHABILITADO : Int := 0;
      ESTADO_SUCIO : Int := 1;
      ESTADO_LIMPIO : Int := 2;
      ESTADO_PRODUCTO : Int := 3;
      ESTADO_CIP : Int := 4;
   END_VAR


BEGIN
	REGION DESCRIPCION
	//=======================================================================
	// (C)Copyright ZeusControl 2022 - 2026
	//=======================================================================
	(*
	### Información del Sistema
	| Hardware Compatible | Entorno de Ingeniería |
	|---------------------|-----------------------|
	| PLC serie 1200/1500 | TIA Portal 18         |
	
	---
	### Descripción Funcional
	El bloque implementa el modelo de dispositivo "Tanque" (`TQ`) para el control integral de depósitos en procesos industriales. Centraliza la adquisición de señales de campo, la gestión de estados del tanque, el cálculo de volumen por geometría cónica-cilíndrica, el control del agitador, el control de temperatura y la supervisión de alarmas, exponiendo toda la información al sistema SCADA mediante direccionamiento multiplexado.
	
	Funcionalidades principales:
	- Gestión del estado global del tanque: deshabilitado, sucio, CIP (limpieza in-situ), limpio y con producto.
	- Lectura y validación de señales digitales y analógicas: detectores de nivel bajo (`LL`), nivel alto (`HL`), boca de hombre, valor de presión y valor de temperatura.
	- Cálculo de la altura de columna de líquido a partir de la presión medida mediante una constante empírica configurable.
	- Cálculo del volumen diferenciando dos tramos geométricos: zona cónica inferior (con radio variable en función del ángulo del cono y radio del cilindro) y zona cilíndrica superior.
	- Detección robusta de tanque vacío y tanque lleno combinando las señales de los detectores con temporizadores (`FC2928_ZC_TON_INDV`) que filtran el volumen por consignas configurables.
	- Gestión de alarmas: nivel bajo, nivel alto, boca de hombre y sobrecapacidad, con agrupación configurable por cuadro para integración en SCADA.
	- Control del agitador con dos modos de operación: fijo (marcha continua) o alternado por tiempos ON/OFF (`FC2926_ZC_FF_INDV`), con consigna de velocidad fija o proporcional al volumen.
	- Control de temperatura en modo calefacción o refrigeración con histéresis configurable y desactivación por nivel mínimo.
	- Seguimiento del producto: registro del producto activo, transición automática entre `Producto1` y `Producto2`, detección de mezcla y borrado al salir del estado PRODUCTO.
	- Soporte de cambio de estado y cambio de producto desde HMI/SCADA mediante flags de orden.
	- Direccionamiento multiplexado para SCADA (no indexado), con reset de índices en el primer arranque del PLC y reubicación del tanque `TQ[0]` cuando el índice vale 0.
	- Contadores de tiempo en estados LIMPIO y PRODUCTO (`FC15025_ZC_CONTADOR_TIEMPO`) para trazabilidad de proceso.
	
	> **Restricciones:** PLC serie 1200/1500.
	
	---
	### Dependencias Requeridas
	| Tipo | Elementos |
	|------|-----------|
	| FC   | `FC15025_ZC_CONTADOR_TIEMPO`, `FC2928_ZC_TON_INDV`, `FC2926_ZC_FF_INDV` |
	| FB   | - |
	| DB   | `DB2040_TQ`, `DB1_SYS` |
	| UDT  | `ZC_DISP_TQ`, `ZC_MUX`, `ZC_TON`, `ZC_CONTADOR_TIEMPO` |
	    
	---
	### Historial de Cambios
	| Versión  | Fecha      | Técnico | Cambios |
	|----------|------------|---------|---------|
	| 01.00.01 | 26.04.2022 | (HCR)   | Primera versión. |
	| 01.00.02 | 09.10.2023 | (HCR)   | Cambio en activación dispositivos para que actúe solamente cuando tenga producto. |
	| 01.00.03 | 15.04.2024 | (ABH)   | Se añade gestión multiplexado para Scada (no indexado). |
	| 01.00.04 | 08.01.2025 | (ABH)   | Se añade entrada de primer arranque del PLC para resetear índices de multiplexado. Se añade movimiento de `TQ[0]` a multiplexado en caso de que el índice sea 0. |
	| 01.00.05 | 24.01.2025 | (ABH)   | Se modifica la lógica del multiplexado, la asignación de `oldindex` se cambia de sitio ya que de vez en cuando (dependiendo de cuándo el HMI realiza la comunicación asíncrona con el PLC) no detectaba el cambio y se machacaban valores. |
	| 01.00.16 | 26.03.2025 | (ABH)   | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
	| 02.00.00 | 17.12.2025 | (ABH)   | Se eliminan los idiomas. Se utiliza códigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se añade `indexHMI` para lista de texto en sistemas de supervisión. |
	*)
	END_REGION DESCRIPCION
	
	
	//  ==========================================================================================================
	REGION LOGICA_DEL_DISPOSITIVO
	    
	    //  Numero maximo de dispositivos para limitar en HMI/Scada
	    "DB2040_TQ".Mux.NumDispositivos := "N_MAX_DISP_TQ";
	    
	    //  Cambio de estado desde HMI/SCADA
	    IF "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioEstado THEN
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueNuevo;
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioEstado := FALSE;
	    END_IF;
	    
	    "DB2040_TQ".TQ[#NumeroTanque].Config_Habilitar := ("DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque <> #ESTADO_DESHABILITADO);
	    
	    // =============================================================================
	    // DISPOSITIVO HABILITADO
	    // =============================================================================    
	    IF "DB2040_TQ".TQ[#NumeroTanque].Config_Habilitar THEN
	        
	        REGION LIMITE_GRUPO_ALARMA
	            
	            IF "DB2040_TQ".TQ[#NumeroTanque].Config_GrupoAlarma < 0 THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Config_GrupoAlarma := 0;
	            END_IF;
	            IF "DB2040_TQ".TQ[#NumeroTanque].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
	            END_IF;
	            
	        END_REGION LIMITE_GRUPO_ALARMA
	        
	        
	        REGION ASIGNAR_ESTADOS
	            
	            // =============================================================================
	            // DETECTOR BOCA DE HOMBRE
	            "DB2040_TQ".TQ[#NumeroTanque].Estado_BocaHombre := #EstadoBocaHombre;
	            // =============================================================================
	            //  DETECTOR LL
	            "DB2040_TQ".TQ[#NumeroTanque].Estado_NivelBajo := #EstadoNivelBajo;
	            // =============================================================================
	            //  DETECTOR HL
	            "DB2040_TQ".TQ[#NumeroTanque].Estado_NivelAlto := #EstadoNivelAlto;
	            // =============================================================================
	            //  LECTURA VALOR NIVEL
	            "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorNivelPresion := #ValorNivelPresion;
	            // =============================================================================
	            //  LECTURA VALOR INGENERIA TEMPERATURA
	            "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura := #ValorTemperatura;
	            
	        END_REGION ASIGNAR_ESTADOS
	        
	        
	        REGION ALARMAS
	            
	            IF "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarAlarmas THEN
	                // =============================================================================
	                //  ALARMA DETECTOR NIVEL MÍNIMO
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo := NOT "DB2040_TQ".TQ[#NumeroTanque].Estado_NivelBajo;
	                // =============================================================================
	                //  ALARMA DETECTOR NIVEL MÁXIMO
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto := "DB2040_TQ".TQ[#NumeroTanque].Estado_NivelAlto;
	                // =============================================================================
	                //  ALARMA DETECTOR BOCA HOMBRE
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_BocaHombre := NOT "DB2040_TQ".TQ[#NumeroTanque].Estado_BocaHombre;
	                // =============================================================================
	                //  ALARMA POR CAPACIDAD
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_Capacidad := "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_CapacidadTanque;
	                // =============================================================================
	                //  ALARMA GENERAL
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_General := "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo OR
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto OR
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_BocaHombre OR
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_Capacidad;
	            ELSE
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_General := FALSE;
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo := FALSE;
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto := FALSE;
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_BocaHombre := FALSE;
	                "DB2040_TQ".TQ[#NumeroTanque].Alarmas_Capacidad := FALSE;
	            END_IF;
	            
	        END_REGION ALARMAS
	        
	        
	        REGION VOLUMEN
	            
	            REGION ESTADO_VOLUMEN
	                
	                // =============================================================================
	                //  POSICIÓN DE ALTURA
	                //  0 - Altura en cono, 1 - Altura por encima del cono
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_PocisionAltura := ("DB2040_TQ".TQ[#NumeroTanque].Estado_Altura > "DB2040_TQ".TQ[#NumeroTanque].Config_AlturaCono);
	                
	                // =============================================================================
	                //  TANQUE VACÍO DETECTOR
	                #t_VacioLL := "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo;
	                // =============================================================================
	                //  TANQUE VACÍO GESTION TON PARA EA < VOLUMEN TANQUE VACIO
	                "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.SP := #TiempoTanqueVacio;
	                "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.IN := ("DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen < "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenTanqueVacio);
	                "FC2928_ZC_TON_INDV"(Pulso1seg := #Pulso1seg,
	                                     TON := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio);
	                #t_VacioEA := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.Q;
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueVacio := #t_VacioLL OR #t_VacioEA;
	                
	                // =============================================================================
	                //  TANQUE LLENO DETECTOR
	                #t_LLenoHL := "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto;
	                // =============================================================================
	                //  TANQUE LLENO GESTION TON PARA EA > VOLUMEN TANQUE LLENO O CAPACIDAD
	                "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueLleno.SP := #TiempoTanqueLLeno;
	                "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueLleno.IN := (("DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenTanqueLleno) OR ("DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_CapacidadTanque));
	                "FC2928_ZC_TON_INDV"(Pulso1seg := #Pulso1seg,
	                                     TON := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueLleno);
	                #t_LlenoEA := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueLleno.Q;
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueLleno := #t_LLenoHL OR #t_LlenoEA;
	                
	            END_REGION ESTADO_VOLUMEN
	            
	            
	            REGION CALCULO_VOLUMEN_TANQUE
	                
	                // =============================================================================
	                // SEÑAL EN AUTOMATICO
	                IF NOT "DB2040_TQ".TQ[#NumeroTanque].Estado_AutoMan THEN
	                    
	                    // =============================================================================
	                    // HABILITADO CALCULO DE VOLUMEN
	                    IF "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarCalculoVol THEN
	                        // =============================================================================
	                        // VALOR PRESION
	                        "DB2040_TQ".TQ[#NumeroTanque].Estado_Presion := "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorNivelPresion;
	                        
	                        // =============================================================================
	                        // CÁLCULO DE COLUMNA DE LÍQUIDO
	                        "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura := "DB2040_TQ".TQ[#NumeroTanque].Estado_Presion * #CONSTANTE_EMPIRICA;
	                        
	                        // =============================================================================
	                        // CÁLCULO DE DIMENSIONES
	                        #t_RadioCilindro := "DB2040_TQ".TQ[#NumeroTanque].Config_DiametroCilindro / 2.0;
	                        #t_RadioConoInf := "DB2040_TQ".TQ[#NumeroTanque].Config_DiametroConoInferior / 2.0;
	                        #t_RadioActual := #t_RadioCilindro;
	                        
	                        // =============================================================================
	                        // ALTURA INFERIOR AL CONO
	                        IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura < "DB2040_TQ".TQ[#NumeroTanque].Config_AlturaCono THEN
	                            #t_RadioActual := ("DB2040_TQ".TQ[#NumeroTanque].Estado_Altura / TAN("DB2040_TQ".TQ[#NumeroTanque].Config_AnguloCono)); //+0.13
	                            // =============================================================================
	                            // LÍMITE RADIO
	                            IF #t_RadioActual > #t_RadioCilindro THEN
	                                #t_RadioActual := #t_RadioCilindro;
	                            END_IF;
	                            
	                            // =============================================================================
	                            // CÁLCULO DE VOLUMEN INFERIOR AL CONO
	                            IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura > 0 THEN
	                                #t_Vol1 := (#t_RadioConoInf * #t_RadioConoInf) + (#t_RadioActual * #t_RadioActual) + (#t_RadioConoInf * #t_RadioActual);
	                                #t_Vol2 := (#CONSTANTE_PI * "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura) / 3.0;
	                                "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := #t_Vol1 * #t_Vol2 * 1000.0;
	                            ELSE
	                                "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := 0.0;
	                            END_IF;
	                            // =============================================================================
	                            // ALTURA SUPERIOR AL CONO   
	                        ELSE
	                            #t_Vol1 := (#t_RadioConoInf * #t_RadioConoInf) + (#t_RadioActual * #t_RadioActual) + (#t_RadioConoInf * #t_RadioActual);
	                            #t_Vol2 := (#CONSTANTE_PI * "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura) / 3.0;
	                            #t_Vol3 := #CONSTANTE_PI * #t_RadioCilindro * #t_RadioCilindro * ("DB2040_TQ".TQ[#NumeroTanque].Estado_Altura - "DB2040_TQ".TQ[#NumeroTanque].Config_AlturaCono);
	                            "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := ((#t_Vol1 * #t_Vol2) + #t_Vol3) * 1000.0;
	                        END_IF;
	                    ELSE
	                        // =============================================================================
	                        // VALOR PRESION
	                        "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorNivelPresion;
	                    END_IF;
	                    "DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaManualVolumen := "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen;
	                ELSE
	                    // =============================================================================
	                    // ESTADO MANUAL DE EA DE NIVEL   
	                    "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := "DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaManualVolumen;
	                END_IF;
	                
	                
	            END_REGION CALCULO_VOLUMEN_TANQUE
	            
	        END_REGION VOLUMEN
	        
	        
	        REGION ESTADO
	            
	            // =============================================================================
	            //  ESTADO SUCIO
	            IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO AND "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueVacio AND #EstadoComOK THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_SUCIO;
	            END_IF;
	            
	            // =============================================================================
	            //  ESTADO CIP
	            IF "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP AND NOT "DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_CIP;
	                "DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP := "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP;
	            END_IF;
	            
	            // =============================================================================
	            //  ESTADO LIMPIO
	            IF NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP AND "DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP THEN
	                IF "DB2040_TQ".TQ[#NumeroTanque].Aux_CipOK THEN
	                    "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_LIMPIO;
	                END_IF;
	                "DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP := "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP;
	            END_IF;
	            
	            IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_CIP AND NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP AND NOT "DB2040_TQ".TQ[#NumeroTanque].Aux_CipOK THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_SUCIO;
	                "DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP := "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP;
	            END_IF;
	            
	            //  CONTADOR TIEMPO LIMPIO
	            "FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1seg,
	                                         Iniciar := "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_LIMPIO,
	                                         Reset := "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque <> #ESTADO_LIMPIO,
	                                         Contador := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_TiempoLimpio);
	            
	            // =============================================================================
	            //  ESTADO PRODUCTO
	            IF "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto AND NOT "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueVacio THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_PRODUCTO;
	            END_IF;
	            
	            
	            // =============================================================================
	            //  CAMBIO DE ESTADO DESDE HMI/SCADA
	            IF "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd1 THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 := "DB2040_TQ".TQ[#NumeroTanque].Estado_ProductoNuevo;
	                "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd1 := FALSE;
	            END_IF;
	            IF "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd2 THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 := "DB2040_TQ".TQ[#NumeroTanque].Estado_ProductoNuevo;
	                
	                "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd2 := FALSE;
	            END_IF;
	            
	            
	            // =============================================================================
	            //  PRODUCTOS
	            IF ("DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO) THEN
	                IF "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto AND NOT "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenProducto THEN
	                    IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 = 0 AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 <> 0 THEN
	                        "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 := "DB2040_TQ".TQ[#NumeroTanque].Orden_TipoProducto;
	                    END_IF;
	                    IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 = 0 THEN
	                        "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 := "DB2040_TQ".TQ[#NumeroTanque].Orden_TipoProducto;
	                    END_IF;
	                END_IF;
	            END_IF;
	            "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenProducto := "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto;
	            
	            IF NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto OR NOT ("DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO) THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenProducto := FALSE;
	            END_IF;
	            
	            "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto := FALSE;
	            
	            //  CONTADOR TIEMPO PRODUCTO
	            "FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1seg,
	                                         Iniciar := "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO,
	                                         Reset := "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque <> #ESTADO_PRODUCTO,
	                                         Contador := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_TiempoProducto);
	            
	            // =============================================================================
	            //  TANQUE MEZCLA
	            "DB2040_TQ".TQ[#NumeroTanque].Estado_Mezcla := "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 <> 0 AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 <> 0 AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 <> "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2;
	            
	            // =============================================================================
	            //  BORRADO DE PRODUCTOS
	            IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque <> #ESTADO_PRODUCTO THEN
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 := 0;
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 := 0;
	            END_IF;
	             
	        END_REGION ESTADO
	        
	        
	        REGION AGITADOR
	            
	            "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Orden_Activar := FALSE;
	            "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarFrecuenciaAgitacion := #HabilitarFrecAgitacion;
	            
	            // =============================================================================
	            // TANQUE CON AGITADOR 
	            // IF "DB2039_TQ".TQ[N_Tanque].Config_HabilitarAgitador AND NOT "DB2039_TQ".TQ[N_Tanque].Orden_ParoControlAgitador THEN
	            IF "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarAgitador THEN
	                
	                // =============================================================================
	                // SE AÑADE 50 LITROS POR ENCIMA PARA EMPEZAR A AGITAR
	                IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlAgitador + #HisteresisAltoNivelAgitacion THEN
	                    "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador := TRUE;
	                END_IF;
	                // =============================================================================
	                //PARO AGITACIÓN POR NIVEL
	                IF "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen < "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlAgitador - #HisteresisBajoNivelAgitacion THEN
	                    "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador := FALSE;
	                END_IF;
	                
	                // =============================================================================
	                //TIPO DE AGITACIÓN POR TIEMPO
	                IF "DB2040_TQ".TQ[#NumeroTanque].Config_TipoAgitacionTiempo = 1 THEN
	                    
	                    "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Config_SetPointTiempoFlip := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONConsigna;
	                    "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Config_SetPointTiempoFlop := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFConsigna;
	                    "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Orden_Pausa := "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlAgitador;
	                    "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Orden_Activar := "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador;
	                    
	                    "FC2926_ZC_FF_INDV"(Pulso1Seg := #Pulso1seg,
	                                        FF := "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador);
	                    
	                    IF "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Estado_Flip THEN
	                        "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Aux_Tiempo;
	                    ELSE
	                        "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := 0;
	                    END_IF;
	                    IF "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Estado_Flop THEN
	                        "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Aux_Tiempo;
	                    ELSE
	                        "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := 0;
	                    END_IF;
	                    
	                ELSE
	                    //  =============================================================================
	                    //  TIPO DE AGITACIÓN FIJO
	                    "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := 0;
	                    "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := 0;
	                END_IF;
	                
	                // =============================================================================
	                //ORDEN ACTIVAR AGITADOR
	                "DB2040_TQ".TQ[#NumeroTanque].Orden_Agitacion :=
	                "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Estado_Flip AND
	                NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlAgitador AND
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_BocaHombre AND
	                "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO;
	                
	                // =============================================================================
	                //FRECUENCIA DE AGITADOR
	                IF "DB2040_TQ".TQ[#NumeroTanque].Orden_Agitacion AND "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarFrecuenciaAgitacion THEN
	                    
	                    IF "DB2040_TQ".TQ[#NumeroTanque].Config_TipoAgitacionVelocidad = 0 THEN
	                        // =============================================================================
	                        //VELOCIDAD FIJA
	                        "DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := "DB2040_TQ".TQ[#NumeroTanque].Config_VelocidadFijaAgitador;
	                        
	                    ELSE
	                        // =============================================================================
	                        //VELOCIDAD POR NIVEL
	                        #t_x0 := "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlAgitador;
	                        #t_x1 := "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMaxControlAgitador;
	                        #t_x := "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen;
	                        #t_y0 := "DB2040_TQ".TQ[#NumeroTanque].Config_VelocidadMinAgitador;
	                        #t_y1 := "DB2040_TQ".TQ[#NumeroTanque].Config_VelocidadMaxAgitador;
	                        #t_ConsignaAgitador := #t_y0 + (#t_y1 - #t_y0) * ((#t_x - #t_x0) / (#t_x1 - #t_x0));
	                        IF #t_ConsignaAgitador > #t_y1 THEN
	                            #t_ConsignaAgitador := #t_y1;
	                        END_IF;
	                        IF #t_ConsignaAgitador < #t_y0 THEN
	                            #t_ConsignaAgitador := #t_y0;
	                        END_IF;
	                        "DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := #t_ConsignaAgitador;
	                    END_IF;
	                ELSE
	                    "DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := 0.0;
	                    
	                END_IF;
	                
	            ELSE
	                "DB2040_TQ".TQ[#NumeroTanque].Orden_Agitacion := FALSE;
	                "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador := FALSE;
	                "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := 0;
	                "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := 0;
	                "DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := 0.0;
	            END_IF;
	            
	        END_REGION AGITADOR
	        
	        
	        REGION VALVULA
	            
	            // =============================================================================
	            // HABILITAR CONTROL DE TEMPERATURA
	            IF "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarControlTemp AND NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlTemp AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO THEN
	                
	                #t_HistPositiva := "DB2040_TQ".TQ[#NumeroTanque].Config_ConsignaTemperatura + "DB2040_TQ".TQ[#NumeroTanque].Config_HisteresisTemperatura;
	                #t_HistNegativa := "DB2040_TQ".TQ[#NumeroTanque].Config_ConsignaTemperatura - "DB2040_TQ".TQ[#NumeroTanque].Config_HisteresisTemperatura;
	                
	                IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlTemp + #HisteresisAltoNivelTemp THEN
	                    "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura := TRUE;
	                    // =============================================================================
	                    // CONTROL DE TEMPERATURA ENFRIAR
	                    IF "DB2040_TQ".TQ[#NumeroTanque].Config_TipoControlTemperatura THEN
	                        
	                        IF "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura > "DB2040_TQ".TQ[#NumeroTanque].Config_ConsignaTemperatura THEN
	                            "DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := TRUE;
	                        END_IF;
	                        
	                        IF "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura < #t_HistNegativa THEN
	                            "DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
	                        END_IF;
	                        // =============================================================================
	                        // CONTROL DE TEMPERATURA CALENTAR
	                    ELSE
	                        
	                        IF "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura < "DB2040_TQ".TQ[#NumeroTanque].Config_ConsignaTemperatura THEN
	                            "DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := TRUE;
	                        END_IF;
	                        
	                        IF "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura > #t_HistPositiva THEN
	                            "DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
	                        END_IF;
	                        
	                    END_IF;
	                END_IF;
	                // =============================================================================
	                //PARAO CONTROL DE TEMPERATURA POR NIVEL
	                IF "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen < "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlTemp - #HisteresisBajoNivelTemp THEN
	                    "DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
	                    "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura := FALSE;
	                END_IF;
	                
	            ELSE
	                "DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
	            END_IF;
	            
	            // =============================================================================
	            //  VISIBILIDAD CONSIGNA DE TEMPERATURA
	            "DB2040_TQ".TQ[#NumeroTanque].Vis_ConsignaTemperatura :=
	            "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarControlTemp AND
	            "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura AND
	            NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP AND ("DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO) AND
	            NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlTemp;
	            
	        END_REGION VALVULA
	        
	    ELSE
	        // =============================================================================
	        //  DISPOSITIVO DESHABILITADO
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_Agitacion := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := 0.0;
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto := FALSE;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioEstado := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd1 := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd2 := FALSE;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenProducto := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador := FALSE;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueVacio := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_PocisionAltura := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_NivelBajo := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_NivelAlto := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_BocaHombre := FALSE;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := 0;  //  Deshabilitado
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 := 0;
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 := 0;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Alarmas_BocaHombre := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Alarmas_Capacidad := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Alarmas_General := FALSE;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := 0.0;
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_Presion := 0.0;
	        "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura := 0.0;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarControlTemp := FALSE;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := 0;
	        "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := 0;
	        "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.ET := 0;
	        "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.Q := FALSE;
	        
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlAgitador := FALSE;
	        "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlTemp := FALSE;
	        
	    END_IF;
	    
	END_REGION LOGICA_DEL_DISPOSITIVO
	
END_FUNCTION

