FUNCTION "FC2927_ZC_FF_SEC_INDV" : Void
TITLE = FC2927_FF_SEC
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion de Flip Flop secuencial
   VAR_INPUT 
      Pulso1Seg : Bool;   // Pulso 1 segundo
   END_VAR

   VAR_IN_OUT 
      FF_SEC : "UDT_ZC_DISP_FF_SEC";
   END_VAR

   VAR_TEMP 
      for_i : Int;
      t_SumaFlip : Int;
   END_VAR

   VAR CONSTANT 
      REPOSO : SInt := 0;
      FLOP : SInt := 10;
      FLIP : SInt := 20;
   END_VAR


BEGIN
	REGION DESCRIPCION
	(*
	###### (C)Copyright ZeusControl 2023 - 2025
	
	---
	### Información del Sistema
	| Hardware Compatible | Entorno de Ingeniería |
	|---------------------|-----------------------|
	| PLC serie 1200/1500 | TIA Portal 18         |
	
	---
	> **Restricciones:** Ninguna.
	
	---
	### Descripción Funcional
	
	Función encargada de gestionar un dispositivo flip flop secuencial.
	
	Las tareas que realiza son las siguientes:
	
	- Gestionar la activacion y calculos de tiempo de cambio entre ordenes Flip y Flop.
	- Adicionalmente, se gestiona tambien la posibilidad de pausar el contaje del tiempo, para evitar su reinicializacion en caso de pausa.
	
	---
	### Dependencias Requeridas
	| Tipo | Elementos |
	|------|-----------|
	| FC   | - |
	| FB   | - |
	| DB   | - |
	| UDT  | `UDT_ZC_DISP_FF_SEC` |
	    
	---
	### Historial de Cambios
	| Versión  | Fecha      | Técnico | Cambios |
	|----------|------------|---------|---------|
	| 00.00.01 | 02.10.2023 | (ABH)   | Primera version. |
	| 00.00.02 | 28.11.2023 | (ABH)   | Se añade condicion de activacion del Flip Flop a que algun SP sea mayor que 0. |
	| 01.00.00 | 15.10.2025 | (ABH)   | Se añade orden pausa para dejar de contar. Refactorizacion de logica de gestion de flips. |
	*)
	END_REGION DESCRIPCION
	
	
	//  ==========================================================================================================
	REGION GESTION_DISPOSITIVO
	    
	    // ==========================================================================================================
	    REGION COMPROBACIONES_INICIALES
	        
	        //  Suma de seguridad por FOR (Determinista)
	        #t_SumaFlip := 0;
	        FOR #for_i := 1 TO 10 DO
	            #t_SumaFlip += #FF_SEC.Config_SetPointTiempoFlip[#for_i];
	        END_FOR;
	        
	        // Si no hay orden o los tiempos globales son 0, reseteamos valores y salimos de la funcion
	        IF NOT #FF_SEC.Orden_Activar OR (#t_SumaFlip <= 0 AND #FF_SEC.Config_SetPointTiempoFlop <= 0) THEN
	            #FF_SEC.Estado_SalidaFlop := FALSE;
	            FOR #for_i := 1 TO 10 DO
	                #FF_SEC.Estado_SalidaFlip[#for_i] := FALSE;
	            END_FOR;
	            
	            #FF_SEC.Aux_Tiempo := 0;
	            #FF_SEC.Estado_Etapa := #REPOSO;
	            #FF_SEC.Aux_FlipActual := 0;
	            #FF_SEC.Aux_Inicializacion := FALSE;
	            RETURN;
	        END_IF;
	        
	    END_REGION COMPROBACIONES_INICIALES
	    
	    
	    // ==========================================================================================================
	    REGION INICIALIZACION
	        
	        //  Arranque inicial forzado a Flop si no hay estado activo
	        IF NOT #FF_SEC.Aux_Inicializacion THEN
	            #FF_SEC.Estado_Etapa := #FLOP;
	            #FF_SEC.Aux_FlipActual := 0;
	            #FF_SEC.Aux_Inicializacion := TRUE;
	        END_IF;
	        
	    END_REGION INICIALIZACION
	        
	        
	        // ==========================================================================================================
	        REGION LOGICA_DE_ESTADOS        
	            
	            CASE #FF_SEC.Estado_Etapa OF
	                    
	                #FLOP:
	                    //  Salidas
	                    #FF_SEC.Estado_SalidaFlop := TRUE;
	                    FOR #for_i := 1 TO 10 DO
	                        #FF_SEC.Estado_SalidaFlip[#for_i] := FALSE;
	                    END_FOR;
	                    
	                    //  Conteo de tiempo
	                    IF #Pulso1Seg AND NOT #FF_SEC.Orden_Pausa THEN
	                        #FF_SEC.Aux_Tiempo += 1;
	                    END_IF;
	                    
	                    //  Transición
	                    IF #FF_SEC.Aux_Tiempo >= #FF_SEC.Config_SetPointTiempoFlop THEN
	                        #FF_SEC.Aux_Tiempo := 0;
	                        #FF_SEC.Aux_FlipActual := 11; // Fallback por defecto
	                        
	                        //  Buscar primer Flip válido
	                        FOR #for_i := 1 TO 10 DO
	                            IF #FF_SEC.Config_SetPointTiempoFlip[#for_i] > 0 THEN
	                                #FF_SEC.Aux_FlipActual := #for_i;
	                                EXIT; // Corta el FOR en el primer acierto
	                            END_IF;
	                        END_FOR;
	                        
	                        //  Transición si encontró algo válido
	                        IF #FF_SEC.Aux_FlipActual <= 10 THEN
	                            #FF_SEC.Estado_Etapa := #FLIP;
	                        END_IF;
	                    END_IF;
	                    
	                #FLIP:
	                    //  Salidas
	                    #FF_SEC.Estado_SalidaFlop := FALSE;
	                    FOR #for_i := 1 TO 10 DO
	                        IF #for_i = #FF_SEC.Aux_FlipActual THEN
	                            #FF_SEC.Estado_SalidaFlip[#for_i] := TRUE;
	                        ELSE
	                            #FF_SEC.Estado_SalidaFlip[#for_i] := FALSE;
	                        END_IF;
	                    END_FOR;
	                    
	                    //  Conteo de tiempo
	                    IF #Pulso1Seg AND NOT #FF_SEC.Orden_Pausa THEN
	                        #FF_SEC.Aux_Tiempo += 1;
	                    END_IF;
	                    
	                    ///  Transición
	                    IF #FF_SEC.Aux_Tiempo >= #FF_SEC.Config_SetPointTiempoFlip[#FF_SEC.Aux_FlipActual] THEN
	                        #FF_SEC.Aux_Tiempo := 0;
	                        #FF_SEC.Aux_FlipActual += 1; // Apunta al siguiente
	                        
	                        // Búsqueda del siguiente Flip válido
	                        FOR #for_i := #FF_SEC.Aux_FlipActual TO 10 DO
	                            IF #FF_SEC.Config_SetPointTiempoFlip[#for_i] > 0 THEN
	                                #FF_SEC.Aux_FlipActual := #for_i;
	                                EXIT;
	                            END_IF;
	                            // Si llega al final sin encontrar nada
	                            IF #for_i = 10 THEN
	                                #FF_SEC.Aux_FlipActual := 11;
	                            END_IF;
	                        END_FOR;
	                        
	                        // Si rebasó el límite, vuelve al Flop
	                        IF #FF_SEC.Aux_FlipActual > 10 THEN
	                            #FF_SEC.Estado_Etapa := #FLOP;
	                        END_IF;
	                    END_IF;
	                    
	                ELSE
	                    #FF_SEC.Estado_Etapa := #REPOSO;
	            END_CASE;
	            
	        END_REGION LOGICA_DE_ESTADOS 
	        
	END_REGION GESTION_DISPOSITIVO
	  
END_FUNCTION

