FUNCTION_BLOCK "FB15050_ZC_CHEQUEO"
TITLE = FB15050_ZC_CHEQUEO
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
   VAR_INPUT 
      GuardarRegistro : Bool;   // Orden guardado registro nuevo
      ComprobarBajoChequeo : Bool;   // Orden comprobacion Alarma bajo chequeo
      ResetBajoChequeo : Bool;   // Reset alarma bajo chequeo
      TomarDatoChequeo : Bool;   // Orden para visualizacion tomar datos en HMI
      PorcentajeBomba : Real;   // Porcentaje bomba
      Temperatura : Real;   // Temperatura
      Caudal : Real;   // Caudal
      CaudalPermeado : Real;   // Caudal permeado
      Conductividad : Real;   // Conductividad
      Presion : Real;   // Presion
      SP_Caudal_100por100 : Real;   // Set Point caudal 100%
      SP_CaudalMinimoChequeo : Real;   // Set Point caudal minimo
      FechaActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;   // Fecha y hora actual
   END_VAR

   VAR_OUTPUT 
      EstadoChequeoActual : Bool;   // Estado chequeo actual
      AlarmaBajoChequeo : Bool;   // Alarma bajo chequeo
      VisDatoChequeo : Bool;   // Visibilidad tomar datos en HMI
      PorcentajeChequeoActual : Real;   // Estado porcentaje actual
   END_VAR

   VAR RETAIN
      s_PorcentajeChequeoOK { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Real := 80.0;
   END_VAR
   VAR 
      s_OrdenGuardarRegistro { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      s_OrdenBorrarRegistros { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
      s_NumeroBorrado { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;   // -1= Todos / Valor= Numero de registro
      s_OldGuardar { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
   END_VAR
   VAR RETAIN
      Registro { S7_SetPoint := 'False'} : Array[1..#N_REGISTROS] of "UDT_ZC_INF_CHEQUEO";
   END_VAR

   VAR_TEMP 
      for_i : Int;
      t_dummyInt : Int;
   END_VAR

   VAR CONSTANT 
      N_REGISTROS : Int := 20;
   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
	El bloque `FB15050_ZC_CHEQUEO` implementa la gestión de los datos de chequeo de un sistema de membranas, manteniendo un histórico circular de hasta `#N_REGISTROS` mediciones y evaluando el rendimiento de la instalación comparando el caudal de permeado real con un valor de referencia al 100% de la bomba.
	
	Funcionalidades principales:
	- Detección de flanco ascendente en la orden de guardado: `#s_OrdenGuardarRegistro` se activa con un pulso de `#GuardarRegistro` mediante la variable estática `#s_P_Trig_Guardar`, evitando registros duplicados por nivel alto mantenido.
	- Adquisición de datos: al recibir la orden de guardado, se desplazan los registros existentes una posición hacia abajo mediante `MOVE_BLK_VARIANT` y se almacena en `#Registro[1]` el conjunto de variables de proceso (`PorcentajeBomba`, `Temperatura`, `Caudal`, `CaudalPermeado`, `Conductividad`, `Presion`), junto con el `ResultadoChequeo` calculado como el porcentaje de caudal de permeado frente a `SP_Caudal_100por100`, y la fecha actual convertida con `FC15005_ZC_DTL_TO_ZC_FECHA` a la estructura `ZC_FECHA`.
	- Cálculo del porcentaje de chequeo en tiempo real: `PorcentajeChequeoActual` se obtiene como el cociente entre el `CaudalPermeado` actual y `SP_Caudal_100por100`, y `EstadoChequeoActual` se activa cuando dicho porcentaje alcanza o supera `#s_PorcentajeChequeoOK`.
	- Alarma de bajo chequeo: se activa `#AlarmaBajoChequeo` cuando `#ComprobarBajoChequeo` está habilitado y el `Caudal` cae por debajo de `#SP_CaudalMinimoChequeo`. Se desactiva con `#ResetBajoChequeo`.
	- Animación de resultados: para cada registro se evalúa `ResultadoAnimacion`, que se activa cuando el `ResultadoChequeo` del registro es igual o superior a 80.0%, permitiendo al HMI colorear el histórico de forma intuitiva.
	- Visibilidad de toma de dato en HMI: `VisDatoChequeo` refleja el estado de `#TomarDatoChequeo` para señalizar al operador que la fase de toma de datos está activa.
	- Borrado de registros selectivo: según el valor de `#s_NumeroBorrado` se distingue entre borrado total (`0` o `-1`), borrado de un registro intermedio (con desplazamiento hacia abajo para mantener la trazabilidad) y borrado del último registro. En todos los casos se inicializan a cero todos los campos del registro (analógicos y de fecha).
	
	> **Restricciones:** Ninguna.
	
	---
	### Dependencias Requeridas
	| Tipo | Elementos |
	|------|-----------|
	| FC   | `FC15005_ZC_DTL_TO_ZC_FECHA` |
	| FB   | - |
	| DB   | - |
	| UDT  | `ZC_FECHA`, `ZC_CHEQUEO` |
	    
	---
	### Historial de Cambios
	| Versión  | Fecha      | Técnico | Cambios |
	|----------|------------|---------|---------|
	| 01.00.00 | 18.07.2023 | (ABH)   | Primera versión. |
	| 01.00.01 | 21.07.2023 | (ABH)   | Se eliminan entradas `Etapa actual` y `tiempo restante`. La gestión de guardado y comprobación de bajo chequeo se realiza mediante entradas BOOL. |
	| 01.00.02 | 11.10.2023 | (HCR)   | Se añaden entradas `TomarDatoChequeo` y salida `VisDatoChequeo` para mostrar en HMI la toma de datos por parte del operador. |
	| 01.00.02 | 03.07.2026 | (ABH)   | Cambio a SCL. |
	*)
	END_REGION DESCRIPCION
	
	
	//  ==========================================================================================================
	REGION ORDEN_GUARDAR
	    
	    //  Orden guardar registro
	    #s_OrdenGuardarRegistro := #GuardarRegistro AND NOT #s_OldGuardar;
	    
	    //  Estado anterior
	    #s_OldGuardar := #GuardarRegistro;
	    
	END_REGION ORDEN_GUARDAR
	
	
	//  ==========================================================================================================
	REGION ADQUISICION_DE_DATOS
	    
	    //  Con flanco de guardar datos, desplazamos los registros y almacenamos los datos del chequeo en la poisicion 1
	    IF #s_OrdenGuardarRegistro THEN
	        
	        //  Desplazamiento de datos
	        #t_dummyInt := MOVE_BLK_VARIANT(SRC := #Registro, COUNT := INT_TO_UDINT(#N_REGISTROS - 1), SRC_INDEX := 0, DEST_INDEX := 1, DEST => #Registro);
	        
	        //  Adquisicion de datos
	        #Registro[1].PorcentajeBomba := #PorcentajeBomba;
	        #Registro[1].Temperatura := #Temperatura;
	        #Registro[1].Caudal := #Caudal;
	        #Registro[1].CaudalPermeado := #CaudalPermeado;
	        #Registro[1].Conductividad := #Conductividad;
	        #Registro[1].Presion := #Presion;
	        #Registro[1].ResultadoChequeo := #CaudalPermeado / #SP_Caudal_100por100 * 100.0;
	        
	        #Registro[1].Fecha := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaActual);
	        
	    END_IF;
	    
	END_REGION ADQUISICION_DE_DATOS
	
	
	//  ==========================================================================================================
	REGION CALCULO_%_CHEQUEO
	    
	    //  Calculo porcentaje del chequeo
	    IF #SP_Caudal_100por100 > 0.0 THEN
	        #PorcentajeChequeoActual := (#Registro[1].CaudalPermeado / #SP_Caudal_100por100) * 100.0;
	    ELSE
	        #PorcentajeChequeoActual := 0.0;
	    END_IF;
	    
	    //  Estado del chequeo actual
	    #EstadoChequeoActual := #PorcentajeChequeoActual >= #s_PorcentajeChequeoOK;
	    
	END_REGION CALCULO_%_CHEQUEO
	
	
	//  ==========================================================================================================
	REGION VISIBILIDADES
	    
	    //  Visibilidad bajo chequeo
	    IF #ComprobarBajoChequeo AND (#Caudal < #SP_CaudalMinimoChequeo) THEN
	        #AlarmaBajoChequeo := TRUE;
	    END_IF;
	    IF #ResetBajoChequeo THEN
	        #AlarmaBajoChequeo := FALSE;
	    END_IF;
	    
	    //  Visibilidad Resultado Chequeo
	    FOR #for_i := 1 TO #N_REGISTROS DO
	        #Registro[#for_i].ResultadoAnimacion := (#Registro[#for_i].ResultadoChequeo >= 80.0);
	    END_FOR;
	    
	    //  Visibilidad tomar dato chequeo en HMI
	    #VisDatoChequeo := #TomarDatoChequeo;
	    
	END_REGION VISIBILIDADES
	
	
	//  ==========================================================================================================
	REGION BORRADO
	    
	    //  Borrado de todos los registros
	    IF #s_OrdenBorrarRegistros THEN
	        IF #s_NumeroBorrado = -1 OR #s_NumeroBorrado = 0 THEN
	            //  ==============================================================================
	            //  Borrado total
	            FOR #for_i := 1 TO #N_REGISTROS DO
	                #Registro[#for_i].PorcentajeBomba := 0.0;
	                #Registro[#for_i].Temperatura := 0.0;
	                #Registro[#for_i].Caudal := 0.0;
	                #Registro[#for_i].CaudalPermeado := 0.0;
	                #Registro[#for_i].Conductividad := 0.0;
	                #Registro[#for_i].Presion := 0.0;
	                #Registro[#for_i].ResultadoChequeo := 0.0;
	                #Registro[#for_i].Fecha.Ano := 0;
	                #Registro[#for_i].Fecha.Mes := 0;
	                #Registro[#for_i].Fecha.Dia := 0;
	                #Registro[#for_i].Fecha.DiaSemana := 0;
	                #Registro[#for_i].Fecha.Hora := 0;
	                #Registro[#for_i].Fecha.Minuto := 0;
	                #Registro[#for_i].Fecha.Segundo := 0;
	                #Registro[#for_i].Fecha.NanoSegundo := 0;
	            END_FOR;
	        ELSIF #s_NumeroBorrado >= 1 AND #s_NumeroBorrado < #N_REGISTROS THEN
	            //  ==============================================================================
	            //  Borrado registro especifico
	            #Registro[#s_NumeroBorrado].PorcentajeBomba := 0.0;
	            #Registro[#s_NumeroBorrado].Temperatura := 0.0;
	            #Registro[#s_NumeroBorrado].Caudal := 0.0;
	            #Registro[#s_NumeroBorrado].CaudalPermeado := 0.0;
	            #Registro[#s_NumeroBorrado].Conductividad := 0.0;
	            #Registro[#s_NumeroBorrado].Presion := 0.0;
	            #Registro[#s_NumeroBorrado].ResultadoChequeo := 0.0;
	            #Registro[#s_NumeroBorrado].Fecha.Ano := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Mes := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Dia := 0;
	            #Registro[#s_NumeroBorrado].Fecha.DiaSemana := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Hora := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Minuto := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Segundo := 0;
	            #Registro[#s_NumeroBorrado].Fecha.NanoSegundo := 0;
	            //  ==============================================================================
	            //  Desplazamiento de datos
	            #t_dummyInt := MOVE_BLK_VARIANT(SRC := #Registro,
	                                            COUNT := INT_TO_UDINT(#N_REGISTROS - #s_NumeroBorrado),
	                                            SRC_INDEX := #s_NumeroBorrado,
	                                            DEST_INDEX := #s_NumeroBorrado - 1,
	                                            DEST => #Registro);
	            //  ==============================================================================
	            //  Borrado del ultimo registro
	            #Registro[#N_REGISTROS].PorcentajeBomba := 0.0;
	            #Registro[#N_REGISTROS].Temperatura := 0.0;
	            #Registro[#N_REGISTROS].Caudal := 0.0;
	            #Registro[#N_REGISTROS].CaudalPermeado := 0.0;
	            #Registro[#N_REGISTROS].Conductividad := 0.0;
	            #Registro[#N_REGISTROS].Presion := 0.0;
	            #Registro[#N_REGISTROS].ResultadoChequeo := 0.0;
	            #Registro[#N_REGISTROS].Fecha.Ano := 0;
	            #Registro[#N_REGISTROS].Fecha.Mes := 0;
	            #Registro[#N_REGISTROS].Fecha.Dia := 0;
	            #Registro[#N_REGISTROS].Fecha.DiaSemana := 0;
	            #Registro[#N_REGISTROS].Fecha.Hora := 0;
	            #Registro[#N_REGISTROS].Fecha.Minuto := 0;
	            #Registro[#N_REGISTROS].Fecha.Segundo := 0;
	            #Registro[#N_REGISTROS].Fecha.NanoSegundo := 0;
	        ELSIF #s_NumeroBorrado = #N_REGISTROS THEN
	            //  ==============================================================================
	            //  Borrado de ultimo registro
	            #Registro[#s_NumeroBorrado].PorcentajeBomba := 0.0;
	            #Registro[#s_NumeroBorrado].Temperatura := 0.0;
	            #Registro[#s_NumeroBorrado].Caudal := 0.0;
	            #Registro[#s_NumeroBorrado].CaudalPermeado := 0.0;
	            #Registro[#s_NumeroBorrado].Conductividad := 0.0;
	            #Registro[#s_NumeroBorrado].Presion := 0.0;
	            #Registro[#s_NumeroBorrado].ResultadoChequeo := 0.0;
	            #Registro[#s_NumeroBorrado].Fecha.Ano := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Mes := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Dia := 0;
	            #Registro[#s_NumeroBorrado].Fecha.DiaSemana := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Hora := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Minuto := 0;
	            #Registro[#s_NumeroBorrado].Fecha.Segundo := 0;
	            #Registro[#s_NumeroBorrado].Fecha.NanoSegundo := 0;
	        END_IF;
	        #s_OrdenBorrarRegistros := FALSE;
	    END_IF;
	    
	END_REGION BORRADO
	
	
END_FUNCTION_BLOCK

