FUNCTION "FC2026_ZC_FF" : Void
TITLE = FC2026_ZC_FF
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion de flip flop
   VAR_INPUT 
      Pulso1Seg : Bool;   // Pulso de 1 segundo
   END_VAR

   VAR_TEMP 
      for_i : Int;
      t_N_MAX : Int;
   END_VAR

   VAR CONSTANT 
      REPOSO : SInt := 0;
      FLOP : SInt := 10;
      PARO1 : SInt := 20;
      FLIP : SInt := 30;
      PARO2 : SInt := 40;
   END_VAR


BEGIN
	REGION DESCRIPCION
	(*
	###### (C)Copyright ZeusControl 2020 - 2026
	
	---
	### 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 con funcion de paro entre cambios.
	
	Las tareas que realiza son las siguientes:
	
	- Gestionar la activacion y calculos de tiempo de cambio entre ordenes Flip, Flop y paro.
	- Adicionalmente, se gestiona tambien la posibilidad de pausar el contaje del tiempo, para evitar su reinicializacion en caso de pausa.
	- En los cambios entre ´flip´ <--> ´flop´, se activa tambien la marca ´paro´ para poder gestionar los cambios y evitar golpes de ariete.
	
	---
	### Dependencias Requeridas
	| Tipo | Elementos |
	|------|-----------|
	| FC   | - |
	| FB   | - |
	| DB   | `DB2026_FF` |
	| UDT  | `UDT_ZC_DISP_FF_PARO` |
	
	---
	### Historial de Cambios
	| Versión  | Fecha      | Técnico | Cambios |
	|----------|------------|---------|---------|
	| 00.00.01 | 10.08.2020 | (ABH)   | Primera version. |
	| 00.00.02 | 02.10.2020 | (ABH)   | Cambio en la logica del dispositivo, ya que al cambio de flip a flop, se perdia un segundo del contaje de tienmpo. |
	| 00.00.03 | 28.11.2023 | (ABH)   | Se añade condicion de activacion del Flip Flop a que algun SP sea mayor que 0.. |
	| 00.00.04 | 15.10.2025 | (ABH)   | Se añade orden pausa para dejar de contar. |
	| 01.00.00 | 06.07.2026 | (ABH)   | Refactorizacion de logica para optimizacion. Se añade ordenes de paro (opcionales) para su uso para evitar golpes de ariete. |
	*)
	END_REGION DESCRIPCION
	
	
	REGION COMPROBACION_N_MAX
	    
	    //  Si no hay dispositivos declarados, salimos del bloque inmediatamente
	    IF "N_MAX_DISP_FF" <= 0 THEN
	        RETURN;
	    END_IF;
	    
	    //  Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
	    #t_N_MAX := "N_MAX_DISP_FF";
	    
	END_REGION COMPROBACION_N_MAX
	
	    
	//  ==========================================================================================================
	REGION GESTION_DISPOSITIVO
	    
	    FOR #for_i := 1 TO #t_N_MAX DO
	        
	        // ==========================================================================================================
	        REGION COMPROBACIONES_INICIALES
	            
	            //  Si no hay orden o faltan tiempos, reseteamos valores y salimos de la funcion
	            IF NOT "DB2026_FF".FF[#for_i].Orden_Activar OR ("DB2026_FF".FF[#for_i].Config_SetPointTiempoFlip <= 0 AND "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlop <= 0) THEN
	                "DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
	                "DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
	                "DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
	                "DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
	                
	                "DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
	                "DB2026_FF".FF[#for_i].Estado_Etapa := #REPOSO;
	                
	                "DB2026_FF".FF[#for_i].Aux_Inicializado := FALSE;
	                RETURN;
	            END_IF;
	            
	        END_REGION COMPROBACIONES_INICIALES
	        
	        
	        // ==========================================================================================================
	        REGION INICIALIZACION
	            
	            //  Arranque inicial forzado a Flop si no hay estado activo
	            IF NOT "DB2026_FF".FF[#for_i].Aux_Inicializado THEN
	                "DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
	                "DB2026_FF".FF[#for_i].Aux_Inicializado := TRUE;
	            END_IF;
	            
	        END_REGION INICIALIZACION
	        
	        
	        // ==========================================================================================================
	        REGION LOGICA_DE_ESTADOS
	            
	            CASE "DB2026_FF".FF[#for_i].Estado_Etapa OF
	                    
	                #FLOP:
	                    //  Salidas
	                    "DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Flop := TRUE;
	                    "DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
	                    
	                    //  Conteo de tiempo
	                    IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
	                        "DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
	                    END_IF;
	                    
	                    //  Transición
	                    IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlop THEN
	                        "DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
	                        //  Si no tenemos tiempo de paro configurado, saltamos a FLIP
	                        IF "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo <= 0 THEN
	                            "DB2026_FF".FF[#for_i].Estado_Etapa := #FLIP;
	                        ELSE
	                            "DB2026_FF".FF[#for_i].Estado_Etapa := #PARO1;
	                        END_IF;
	                    END_IF;
	                    
	                #PARO1:
	                    //  Salidas
	                    "DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Paro1 := TRUE;
	                    "DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
	                    
	                    //  Conteo de tiempo
	                    IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
	                        "DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
	                    END_IF;
	                    
	                    //  Transición
	                    IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo THEN
	                        "DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
	                        "DB2026_FF".FF[#for_i].Estado_Etapa := #FLIP;
	                    END_IF;
	                    
	                    
	                #FLIP:
	                    //  Salidas
	                    "DB2026_FF".FF[#for_i].Estado_Flip := TRUE;
	                    "DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
	                    
	                    //  Conteo de tiempo
	                    IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
	                        "DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
	                    END_IF;
	                    
	                    //  Transición
	                    IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlip THEN
	                        "DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
	                        //  Si no tenemos tiempo de paro configurado, saltamos a FLOP
	                        IF "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo <= 0 THEN
	                            "DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
	                        ELSE
	                            "DB2026_FF".FF[#for_i].Estado_Etapa := #PARO2;
	                        END_IF;
	                    END_IF;
	                    
	                    
	                #PARO2:
	                    //  Salidas
	                    "DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
	                    "DB2026_FF".FF[#for_i].Estado_Paro2 := TRUE;
	                    
	                    //  Conteo de tiempo
	                    IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
	                        "DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
	                    END_IF;
	                    
	                    //  Transición
	                    IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo THEN
	                        "DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
	                        "DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
	                    END_IF;
	                    
	                ELSE
	                    "DB2026_FF".FF[#for_i].Estado_Etapa := #REPOSO;
	            END_CASE;
	            
	        END_REGION LOGICA_DE_ESTADOS 
	        
	    END_FOR;
	
	END_REGION GESTION_DISPOSITIVO
	
	
END_FUNCTION

