FUNCTION "FC2041_ZC_TQ_AE" : Void
TITLE = FC2041_ZC_TQ_AE
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : HCR
FAMILY : ZeusControl
VERSION : 1.0
   VAR_INPUT 
      Pulso1seg : Bool;
      Arranque : Bool;
      Ack : Bool;
   END_VAR

   VAR_TEMP 
      for_i : Int;
      t_N_MAX : Int;
      t_Index : Int;
      t_oldIndex : Int;
      t_CambioDesdeHMI : Bool;
      t_CambioDesdeMux : Bool;
   END_VAR


BEGIN
	REGION DESCRIPCION
	//=======================================================================
	// (C)Copyright ZeusControl 2024 - 2026
	//=======================================================================
	(*
	### Información del Sistema
	| Hardware Compatible | Entorno de Ingeniería |
	|---------------------|-----------------------|
	| PLC serie 1200/1500 | TIA Portal 18 |
	  
	---
	### Descripción Funcional
	Bloque de gestión de dispositivos `TQ Aire Estéril`. Realiza la lógica de control, supervisión, alarmas y trazabilidad de los tanques de aire estéril de la planta, así como el multiplexado de los dispositivos hacia los distintos clientes HMI/SCADA.
	
	> **Restricciones:** Ninguna.
	
	Acciones detalladas:
	- Inicializa los índices de multiplexado a `0` en el arranque del PLC.
	- Gestiona los límites del índice (`Index`) y el flag `MaxVisible` para cada cliente HMI/SCADA.
	- Realiza la carga inicial del dispositivo seleccionado al detectar un cambio de índice.
	- Sincroniza los datos de lectura del dispositivo hacia la estructura del multiplexado (`Mux[...]`).
	- Sincroniza los datos de escritura desde el multiplexado hacia el dispositivo, marcando `ExisteCambio` cuando se modifica algún valor.
	- Propaga los cambios realizados desde SCADA al resto de multiplexados que tengan seleccionado el mismo dispositivo.
	- Resetea los flags `AlgunaAlarma` y `AlgunaEnManual` de las agrupaciones de alarma en cada ciclo.
	- Gestiona los modos de trabajo `AutoMan` y la orden `Orden_Manual`.
	- Gestiona las alarmas de configuración, presión baja y presión alta, con temporización de confirmación mediante `Pulso1seg`.
	- Controla la válvula de ducha durante el ciclo CIP mediante lógica flip-flop temporizada.
	- Controla la válvula de venteo, inhibida por las alarmas de presión.
	- Controla la válvula de estéril de forma modulante en función de la presión de trabajo.
	- Registra la trazabilidad de los cambios de modo Auto/Manual y de la activación/desactivación de la orden manual mediante `FC8_ZC_TRAZA_REGISTRO`.
	
	---
	### Dependencias Requeridas
	| Tipo | Elementos |
	|------|-----------|
	| FC   | `FC8_ZC_TRAZA_REGISTRO` |
	| FB   | - |
	| DB   | `DB2041_ZC_TQ_AE` |
	| UDT  | `ZC_DISP_TQ_AE` |
	    
	---
	### Historial de Cambios
	| Versión  | Fecha      | Técnico | Cambios |
	|----------|------------|---------|---------|
	| 00.00.01 | 15.10.2024 | (HCR)   | Primera versión. |
	| 00.00.02 | 28.01.2025 | (ABH)   | Se añade estado AutoMan y orden manual. Se añade trazabilidad. Se añade multiplexado. |
	| 00.00.03 | 26.03.2025 | (ABH)   | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
	| 00.00.04 | 24.04.2025 | (HCR)   | Se añade gestión de idioma. |
	| 00.00.05 | 11.09.2025 | (ABH)   | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traducción. |
	| 00.00.06 | 17.12.2025 | (ABH)   | Se eliminan los idiomas. Se utiliza códigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. |
	| 01.00.00 | 08.07.2026 | (ABH)   | Se extrae el multiplexado a FC independiente. |
	*)
	END_REGION DESCRIPCION
	
	
	REGION LOGICA_DEL_DISPOSITIVO
	    
	    // =============================================================================
	    // Resetear todas las alarmas para que se vuelvan a generar
	    FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
	        "DB2041_TQ_AE".Agrup[#for_i].AlgunaAlarma := FALSE;
	        "DB2041_TQ_AE".Agrup[#for_i].AlgunaEnManual := FALSE;
	    END_FOR;
	    
	    //  Numero maximo de dispositivos para limitar en HMI/Scada
	    "DB2041_TQ_AE".Mux.NumDispositivos := "N_MAX_DISP_TQ_AE";
	    
	    REGION COMPROBACION_N_MAX
	        
	        //  Si no hay dispositivos declarados, salimos del bloque inmediatamente
	        IF "N_MAX_DISP_TQ_AE" <= 0 THEN
	            RETURN;
	        END_IF;
	        
	        //  Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
	        #t_N_MAX := "N_MAX_DISP_TQ_AE";
	        
	    END_REGION COMPROBACION_N_MAX
	    
	    FOR #for_i := 1 TO #t_N_MAX DO
	        
	        
	        IF "DB2041_TQ_AE".TQ_AE[#for_i].Config_Habilitar THEN
	            
	            REGION MODOS_DE_TRABAJO
	                
	                // =============================================================================
	                //  MODO MANUAL
	                IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan THEN
	                    "DB2041_TQ_AE".Agrup["DB2041_TQ_AE".TQ_AE[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
	                END_IF;
	                
	                // =============================================================================
	                //  MODO AUTO
	                IF NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan THEN
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual := FALSE;
	                END_IF;
	                
	            END_REGION
	            
	            
	            REGION ALARMAS
	                
	                IF "DB2041_TQ_AE".TQ_AE[#for_i].Config_HabilitarAlarmas THEN
	                    
	                    // =============================================================================
	                    //  ALARMA DE CONFIGURACION
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion := TRUE;
	                    IF ("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinAlarma < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo) AND
	                        ("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo) AND
	                        ("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxAlarma) THEN
	                        "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion := FALSE;
	                    END_IF;
	                    
	                    // =============================================================================
	                    //  ALARMA MINIMA PRESION
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := FALSE;
	                    IF ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinAlarma)
	                        AND
	                        NOT #Ack
	                    THEN
	                        IF ("DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointAlarmaMinPresion) AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Vaciado THEN
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := TRUE;
	                        ELSE
	                            IF #Pulso1seg THEN
	                                "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion += 1;
	                            END_IF;
	                        END_IF;
	                    ELSE
	                        "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion := 0;
	                    END_IF;
	                    
	                    // =============================================================================
	                    //  ALARMA MAXIMA PRESION
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta := FALSE;
	                    IF ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion > "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxAlarma)
	                        
	                    THEN
	                        IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointAlarmaMaxPresion THEN
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta := TRUE;
	                        ELSE
	                            IF #Pulso1seg THEN
	                                "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion += 1;
	                            END_IF;
	                        END_IF;
	                    ELSE
	                        "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion := 0;
	                    END_IF;
	                    
	                ELSE
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion :=
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta :=
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := FALSE;
	                END_IF;
	                
	            END_REGION
	            
	            
	            REGION VALVULA_DUCHA
	                
	                IF "DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP AND
	                    ("DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlipDucha > 0
	                    OR
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlopDucha > 0)
	                THEN
	                    
	                    IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlanco = FALSE THEN
	                        "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := TRUE;
	                        "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlanco := TRUE;
	                    END_IF;
	                    
	                    //  =============================================================================
	                    //  GESTION TIEMPO FLIP FLOP
	                    IF #Pulso1seg THEN
	                        
	                        //  =============================================================================
	                        //  CUENTA FLIP
	                        IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo = TRUE THEN
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip += 1;
	                        END_IF;
	                        
	                        //  =============================================================================
	                        //  CUENTA FLOP
	                        IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo = TRUE THEN
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop += 1;
	                        END_IF;
	                        
	                        //  =============================================================================
	                        //  GESTION LOGICA Y SALIDAS FLIP
	                        IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlipDucha THEN
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip := FALSE;
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := TRUE;
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip := 0;
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo := false;
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo := true;
	                        END_IF;
	                        
	                        //  =============================================================================
	                        //  GESTION LOGICA Y SALIDAS FLOP
	                        IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlopDucha THEN
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip := TRUE;
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := FALSE;
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop := 0;
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo := true;
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo := false;
	                        END_IF;
	                        
	                    END_IF;
	                    
	                END_IF;
	                
	                //  =============================================================================
	                //  GESTION LOGICA SALIDA DUCHA
	                "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaDucha :=
	                
	                (
	                NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan
	                AND
	                (
	                "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion
	                OR
	                ("DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip)
	                )
	                )
	                OR
	                ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual);
	                
	                
	            END_REGION
	            
	            
	            REGION VALVULA_VENTEO
	                
	                //  =============================================================================
	                //  GESTION LOGICA SALIDA VENTEO
	                "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaVenteo :=
	                (
	                (NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion)
	                OR
	                ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual)
	                )
	                AND
	                NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja;
	                
	            END_REGION
	            
	            
	            REGION VALVULA_ESTERIL
	                
	                //  =============================================================================
	                //  GESTION LOGICA SALIDA ESTERIL
	                IF (NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion)
	                    OR
	                    ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual)
	                THEN
	                    IF NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja THEN
	                        IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion <= "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo THEN
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := TRUE;
	                        END_IF;
	                        IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo THEN
	                            "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
	                        END_IF;
	                        
	                    ELSE
	                        "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
	                    END_IF;
	                ELSE
	                    "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
	                END_IF;
	                
	            END_REGION
	            
	            
	            REGION TRAZABILIDAD_MANUALIZACION
	                
	                IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan THEN
	                    "FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
	                                            Registrar := 1,
	                                            Categoria := "TRZ_CAT_20_DISP_TQ_AE",
	                                            User := "DB1_SYS".Usuario.Actual,
	                                            CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
	                                            CodInt_2 := #for_i,
	                                            CodInt_3 := "TRZ_SYS_DISP_ACC_MAN",
	                                            CodInt_4 := 0,
	                                            CodInt_5 := 0,
	                                            CodReal_1 := 0.0,
	                                            CodReal_2 := 0.0);
	                END_IF;
	                
	                IF NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan THEN
	                    "FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
	                                            Registrar := 1,
	                                            Categoria := "TRZ_CAT_20_DISP_TQ_AE",
	                                            User := "DB1_SYS".Usuario.Actual,
	                                            CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
	                                            CodInt_2 := #for_i,
	                                            CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
	                                            CodInt_4 := 0,
	                                            CodInt_5 := 0,
	                                            CodReal_1 := 0.0,
	                                            CodReal_2 := 0.0);
	                END_IF;
	                
	                IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual THEN
	                    "FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
	                                            Registrar := 1,
	                                            Categoria := "TRZ_CAT_20_DISP_TQ_AE",
	                                            User := "DB1_SYS".Usuario.Actual,
	                                            CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
	                                            CodInt_2 := #for_i,
	                                            CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
	                                            CodInt_4 := 0,
	                                            CodInt_5 := 0,
	                                            CodReal_1 := 0.0,
	                                            CodReal_2 := 0.0);
	                END_IF;
	                
	                IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual THEN
	                    "FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
	                                            Registrar := 1,
	                                            Categoria := "TRZ_CAT_20_DISP_TQ_AE",
	                                            User := "DB1_SYS".Usuario.Actual,
	                                            CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
	                                            CodInt_2 := #for_i,
	                                            CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
	                                            CodInt_4 := 0,
	                                            CodInt_5 := 0,
	                                            CodReal_1 := 0.0,
	                                            CodReal_2 := 0.0);
	                END_IF;
	                
	            END_REGION
	            
	            
	            // =============================================================================
	            //  Estado anterior manual/automatico
	            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual := "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual;
	            "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan := "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan;
	            
	        ELSE
	            
	            // =============================================================================
	            //  INICIALIZACIÓN DE SALIDAS
	            "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Vaciado :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaDucha :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril :=
	            "DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaVenteo := FALSE;
	            
	        END_IF;
	        
	    END_FOR;
	    
	END_REGION
	
	
END_FUNCTION

