FUNCTION_BLOCK "FB15049_ZC_AIRE_ESTERIL"
TITLE = FB15049_ZC_AIRE_ESTERIL
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : HCR
FAMILY : ZeusControl
VERSION : 1.0
   VAR_INPUT 
      Pulso1Seg : Bool;   // Pulso 1 Segundo
      HabilitarMantenimiento : Bool;
      AlarmaFalloVapor : Bool;
      FechaHora {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;   // Fecha y hora actual
      TempEsterilAlcanzada : Bool;   // Estado temperatura de esterilizacion alcanzada
      SP_TiempoEsterilizacion : Int;   // SP_Tiempo de esterilizacion
      SP_TiempoEnfriamiento : Int;   // SP_Tiempo de enfriamiento
      SP_NumeroMaximoEsterilizacion : Int;
      SP_DiasMaximoFiltroEsteril : Int;   // SP_Dias Maximo Filtro Esteril
      ProcesoID : Int;   // ID Proceso para trazabilidad
   END_VAR
   VAR_INPUT RETAIN
      Usuario : String[25];   // Usuario actual
   END_VAR
   VAR_INPUT 
      MinutoAlarma : USInt;
      SegundoAlarma : USInt;
   END_VAR

   VAR_IN_OUT 
      ParoMantenimiento : Bool;
      AvisoDiasEsterilizacion : Bool;
      AlarmaDiasEsterilizacion : Bool;
   END_VAR

   VAR 
      s_Habilitar : Bool;   // Estado habilitado
      s_FiltroEsteril : Bool;   // Estado filtro esteril
   END_VAR
   VAR RETAIN
      s_Etapa : Int;   // Etapa actual
      s_NumeroEsterilizaciones : Int;   // Numero de esterilizaciones
   END_VAR
   VAR 
      S_NumeroDiasEsterilizacion : Int;   // Numerpo de dias con filtro esterilizado
   END_VAR
   VAR RETAIN
      s_TiempoEtapa { S7_SetPoint := 'False'} : "UDT_ZC_PRO_TIEMPO_ETAPA";   // Tiempo de etapa
   END_VAR
   VAR 
      Pulsadores : Struct
         Habilitar { S7_SetPoint := 'False'} : "UDT_ZC_PULSADOR";
         Deshabilitar { S7_SetPoint := 'False'} : "UDT_ZC_PULSADOR";
         Mantenimiento { S7_SetPoint := 'False'} : "UDT_ZC_PULSADOR";
      END_STRUCT;
   END_VAR
   VAR RETAIN
      TiempoEsterelizado : "UDT_ZC_CONTADOR_TIEMPO";
   END_VAR
   VAR 
      s_PasswordCalculado { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt;   // Contraseña calculada de mantenimiento
      s_PasswordHMI : UInt;   // Contraseña calculada de mantenimiento
      s_MostrarAlarmaMto { S7_SetPoint := 'True'} : Bool;
      s_oldMostrarAlarmaMto { S7_SetPoint := 'True'} : Bool;
   END_VAR

   VAR_TEMP 
      t_ResetContador : Bool;   // Tempoeral reset contador de horas
      t_DiasAvisos : Int;
   END_VAR

   VAR CONSTANT 
      REPOSO : Int := 0;
      ESTERILIZANDO : Int := 10;
      ESTERILIZADO : Int := 20;
      ENFRIAMIENTO : Int := 30;
   END_VAR


BEGIN
	REGION DESCRIPCION
	//=======================================================================
	// (C)Copyright ZeusControl 2024 - 202
	//=======================================================================
	(*
	### Información del Sistema
	| Hardware Compatible | Entorno de Ingeniería |
	|---------------------|-----------------------|
	| PLC serie 1200/1500 | TIA Portal 18         |
	
	---
	### Descripción Funcional
	Función principal para la gestión integral y secuencial del dispositivo del tanque de aire estéril.
	
	> **Restricciones:** Ninguna.
	
	- Evaluar y gestionar las órdenes temporizadas provenientes de los pulsadores del HMI (`Habilitar`, `Deshabilitar`, `Mantenimiento`) mediante la invocación de `FC15026_ZC_PULSADOR`.
	- Ejecutar la máquina de estados principal para controlar el ciclo de vida y las fases operativas del sistema: `REPOSO`, `ESTERILIZANDO`, `ENFRIAMIENTO` y `ESTERILIZADO`.
	- Contabilizar el tiempo de permanencia en estado estéril utilizando `FC15025_ZC_CONTADOR_TIEMPO`, llevando el registro acumulado de días de operación del filtro.
	- Monitorizar dinámicamente los tiempos de cada etapa (`s_TiempoEtapa`) y validar las transiciones de estado en función de la temperatura (`TempEsterilAlcanzada`) o la detección de fallos (`AlarmaFalloVapor`).
	- Revocar automáticamente el estado de filtro estéril (`s_FiltroEsteril`) al superar el límite máximo de días configurado en `SP_DiasMaximoFiltroEsteril`.
	- Procesar las rutinas de parada por mantenimiento preventivo, exigiendo validación de seguridad mediante contraseña (`s_PasswordHMI` vs `s_PasswordCalculado`) una vez alcanzado el límite de esterilizaciones (`SP_NumeroMaximoEsterilizacion`).
	- Calcular y emitir señales preventivas de aviso (`AvisoDiasEsterilizacion`) y alarma crítica (`AlarmaDiasEsterilizacion`) según la caducidad operativa del filtro estéril.
	
	---
	### Dependencias Requeridas
	| Tipo | Elementos |
	|------|-----------|
	| FC   | `FC15025_ZC_CONTADOR_TIEMPO`, `FC15026_ZC_PULSADOR` |
	| FB   | - |
	| DB   | `DB1_SYS` |
	| UDT  | `UDT_ZC_CONTADOR_TIEMPO`, `UDT_ZC_PRO_TIEMPO_ETAPA`, `UDT_ZC_PULSADOR` |
	    
	---
	### Historial de Cambios
	| Versión  | Fecha      | Técnico | Cambios |
	|----------|------------|---------|---------|
	| 01.00.00 | 15.10.2024 | (HCR) | Primera version. |
	| 01.00.01 | 28.01.2025 | (ABH) | Se cambia organizacion de variables y nombres para estandarizacion. |
	*)
	END_REGION DESCRIPCION
	
	
	//  ==========================================================================================================
	REGION PULSADORES
	    
	    //  Permiso pulsadores
	    #Pulsadores.Habilitar.Permiso := NOT #s_Habilitar;
	    #Pulsadores.Deshabilitar.Permiso := #s_Habilitar;
	    #Pulsadores.Mantenimiento.Permiso := #ParoMantenimiento;
	    
	    //  Gestion pulsadores
	    "FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
	                          SP_Tiempo := 2,
	                          TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
	                          TrazaIdProceso:=#ProcesoID,
	                          TrazaIdPulsador:="TRZ_SYS_PUL_AE_HAB",
	                          Usuario := #Usuario,
	                          FechaHoraActual := #FechaHora,
	                          Pulsador := #Pulsadores.Habilitar);
	    "FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
	                          SP_Tiempo := 2,
	                          TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
	                          TrazaIdProceso:=#ProcesoID,
	                          TrazaIdPulsador:="TRZ_SYS_PUL_AE_DES",
	                          Usuario := #Usuario,
	                          FechaHoraActual := #FechaHora,
	                          Pulsador := #Pulsadores.Deshabilitar);
	    "FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
	                          SP_Tiempo := 2,
	                          TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
	                          TrazaIdProceso:=#ProcesoID,
	                          TrazaIdPulsador:="TRZ_SYS_PUL_AE_MNT",
	                          Usuario := #Usuario,
	                          FechaHoraActual := #FechaHora,
	                          Pulsador := #Pulsadores.Mantenimiento);
	    
	    //  Orden habilitar aire esteril
	    IF #Pulsadores.Habilitar.Plc THEN
	        #s_Habilitar := TRUE;
	    ELSIF #Pulsadores.Deshabilitar.Plc THEN
	        #s_Habilitar := FALSE;
	    END_IF;
	    
	END_REGION PULSADORES
	
	
	//  ==========================================================================================================
	REGION SECUENCIA_FILTRO  
	    
	    CASE #s_Etapa OF
	            
	            // =============================================================================    
	        #REPOSO:
	            IF #s_Habilitar THEN
	                #s_Etapa := #ESTERILIZADO;
	                #s_TiempoEtapa.Actual := 0;
	            END_IF;
	            
	            // =============================================================================    
	        #ESTERILIZANDO:
	            #s_TiempoEtapa.SP := #SP_TiempoEsterilizacion;
	            
	            IF NOT #s_Habilitar THEN
	                #s_Etapa := #REPOSO;
	            END_IF;
	            IF #AlarmaFalloVapor THEN
	                #s_Etapa := #REPOSO;
	            END_IF;
	            IF #s_TiempoEtapa.Cumplido THEN
	                #s_Etapa := #ENFRIAMIENTO;
	                #s_TiempoEtapa.Actual := 0;
	                #S_NumeroDiasEsterilizacion := 0;
	                #s_NumeroEsterilizaciones += 1;
	                #s_FiltroEsteril := TRUE;
	               
	            END_IF;
	            IF #TempEsterilAlcanzada THEN
	                #s_TiempoEtapa.Activar := TRUE;
	            ELSE
	                #s_TiempoEtapa.Activar := FALSE;
	                #s_TiempoEtapa.Actual := 0;
	            END_IF;
	            // =============================================================================              
	        #ENFRIAMIENTO:
	            #s_TiempoEtapa.SP := #SP_TiempoEnfriamiento;
	            #s_TiempoEtapa.Activar := TRUE;
	            
	            IF NOT #s_Habilitar THEN
	                #s_Etapa := #REPOSO;
	            END_IF;
	            IF #s_TiempoEtapa.Cumplido THEN
	                #s_Etapa := #ESTERILIZADO;
	                #s_TiempoEtapa.Actual := 0;
	            END_IF;
	            // ============================================================================= 
	        #ESTERILIZADO:
	            #s_TiempoEtapa.SP := 5;
	            IF NOT #s_Habilitar THEN
	                #s_Etapa := #REPOSO;
	            END_IF;
	            IF #s_TiempoEtapa.Cumplido THEN
	                #s_Etapa := #ESTERILIZANDO;
	                #s_TiempoEtapa.Actual := 0;
	            END_IF;
	            IF #TempEsterilAlcanzada AND NOT #s_FiltroEsteril THEN
	                #s_TiempoEtapa.Activar := TRUE;
	            ELSE
	                #s_TiempoEtapa.Activar := FALSE;
	                #s_TiempoEtapa.Actual := 0;
	            END_IF;
	        ELSE
	            #s_Etapa := #REPOSO;
	    END_CASE;
	    
	END_REGION SECUENCIA_FILTRO  
	
	
	//  ==========================================================================================================
	REGION TIEMPO_ESTERILIZADO
	    
	    //  Incrementar dias de filtro #ESTERILIZADO
	    IF #TiempoEsterelizado.Hora >= 24 THEN
	        #t_ResetContador := TRUE;
	        #S_NumeroDiasEsterilizacion += 1;
	    END_IF;
	    
	    "FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg:=#Pulso1Seg,
	                                 Iniciar:=#Pulso1Seg AND (#s_Etapa = #ESTERILIZADO),
	                                 Reset:=#t_ResetContador,
	                                 Contador:=#TiempoEsterelizado);
	    
	    //  Quitar estado filtro esteril
	    IF #S_NumeroDiasEsterilizacion >= #SP_DiasMaximoFiltroEsteril THEN
	        #s_FiltroEsteril := FALSE;
	    END_IF;
	    
	END_REGION TIEMPO_ESTERILIZADO
	
	
	//  ==========================================================================================================
	REGION TIEMPO_ETAPA
	    
	    //  Proceso en estado parado
	    IF #s_Etapa = #REPOSO THEN
	        #s_TiempoEtapa.Actual := 0;
	        #s_TiempoEtapa.Activar := FALSE;
	    END_IF;
	    
	    //  Temporizador activo
	    IF #s_TiempoEtapa.Activar THEN
	        IF #Pulso1Seg
	            AND
	            (#s_TiempoEtapa.Actual < #s_TiempoEtapa.SP)
	        THEN
	            #s_TiempoEtapa.Actual += 1;
	        END_IF;
	        
	        //  Reiniciar tiempo paso cumplido para esperar condicion
	        #s_TiempoEtapa.Cumplido := FALSE;
	        
	        //  Tiempo paso cumplido 
	        IF #s_TiempoEtapa.Actual >= #s_TiempoEtapa.SP AND #s_TiempoEtapa.Activar THEN
	            #s_TiempoEtapa.Cumplido := TRUE;
	        END_IF;
	        
	        //  Tiempo restante
	        #s_TiempoEtapa.Restante := #s_TiempoEtapa.SP - #s_TiempoEtapa.Actual;
	    END_IF;
	    
	    //  Tiempo restante
	    #s_TiempoEtapa.Restante := #s_TiempoEtapa.SP - #s_TiempoEtapa.Actual;
	    
	END_REGION TIEMPO_ETAPA
	
	
	//  ==========================================================================================================
	REGION MANTENIMIENTO
	    
	    //  Flanco mostrar cartel de mantenieminto
	    IF #HabilitarMantenimiento THEN
	        
	        #s_MostrarAlarmaMto := (#FechaHora.MINUTE = #MinutoAlarma) AND (#FechaHora.SECOND = #SegundoAlarma) AND (#s_NumeroEsterilizaciones >= #SP_NumeroMaximoEsterilizacion);
	        
	        IF #s_MostrarAlarmaMto AND NOT #s_oldMostrarAlarmaMto THEN
	            #ParoMantenimiento := TRUE;
	            #s_PasswordCalculado :=
	            #s_PasswordHMI := 0;
	        END_IF;
	        
	        //  Pulsador chequear contraseña de mantenimiento
	        IF #Pulsadores.Mantenimiento.Plc THEN
	            #s_PasswordCalculado := #FechaHora.YEAR + #FechaHora.MONTH + #FechaHora.DAY;
	            IF #s_PasswordCalculado = #s_PasswordHMI THEN
	                #s_NumeroEsterilizaciones := 0;
	                #S_NumeroDiasEsterilizacion := 0;
	            END_IF;
	            #s_PasswordCalculado :=
	            #s_PasswordHMI := 0;
	            #ParoMantenimiento := FALSE;
	        END_IF;
	    ELSE
	        #ParoMantenimiento := FALSE;
	    END_IF;
	    
	    #s_oldMostrarAlarmaMto := #s_MostrarAlarmaMto;
	    
	END_REGION MANTENIMIENTO
	
	
	//  ==========================================================================================================
	REGION ALARMAS
	    
	    //  Aviso un dia antes  
	    #t_DiasAvisos := #SP_DiasMaximoFiltroEsteril - 1;
	    IF #t_DiasAvisos < 0 THEN
	        #t_DiasAvisos := 0;
	    END_IF;
	    
	    //  Alarmas por dia de esterilizacion
	    #AlarmaDiasEsterilizacion := (#S_NumeroDiasEsterilizacion >= #SP_DiasMaximoFiltroEsteril);
	    #AvisoDiasEsterilizacion := (#S_NumeroDiasEsterilizacion >= #t_DiasAvisos AND NOT #AlarmaDiasEsterilizacion);
	    
	END_REGION ALARMAS
	
	
	//  ==========================================================================================================
	REGION NUMERO_ESTERILIZACIONES
	    
	    IF #s_NumeroEsterilizaciones = 0 THEN
	        #s_NumeroEsterilizaciones += 1;
	    END_IF;
	    
	END_REGION NUMERO_ESTERILIZACIONES
	
END_FUNCTION_BLOCK

