FUNCTION_BLOCK "FB15052_ZC_INF_PROD_MEM"
TITLE = FB15052_ZC_INF_PROD_MEM
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Bloque de gestion para adquisicion de datos para informe de produccion maquina membranas
   VAR_INPUT 
      OrdenInicioInforme { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;   // Orden inicio informe
      OrdenFinInforme : Bool;   // Orden fin informe
      EnProduccion : Bool;   // En produccion
      FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DTL;   // Fecha y hora actual
      Producto : Int;   // Producto
      DestinoConcentrado : Int;   // Destino concentrado
      DestinoPermeado : Int;   // Destino permeado
      LitrosEntrada : LReal;   // Totalizador litros Entrada
      LitrosConcentrado : LReal;   // Totalizador litros Concentrado
      Brix : LReal;   // Media Brix en produccion
      Fcv : Real;   // Media FCV En produccion
      TempEntrada : LReal;   // Media temperatura entrada
      TempConcentrado : LReal;   // Media temperatura concentrado
      TempPermeado : LReal;   // Media temperatura permeado
      TiempoProduccion : "UDT_ZC_CONTADOR_TIEMPO";   // Tiempo Produccion
   END_VAR

   VAR_IN_OUT 
      Informe : Array[*] of "UDT_ZC_INF_PROD_MEM";
   END_VAR

   VAR 
      s_OrdenInicioInforme { S7_SetPoint := 'True'} : Bool;   // Orden inicio informe
      s_OrdenFinInforme { S7_SetPoint := 'True'} : Bool;   // Orden fin informe
   END_VAR
   VAR RETAIN
      s_oldInicio { S7_SetPoint := 'True'} : Bool;   // Estado anterior inicio
      s_oldFin { S7_SetPoint := 'True'} : Bool;   // Estado anterior fin
   END_VAR
   VAR 
      s_OrdenBorrarInforme : Bool;   // Orden borrar informe
      s_NumeroBorrado : Int;   // -1= Todos / Valor= Numero de registro
   END_VAR

   VAR_TEMP 
      for_i : Int;
      t_dummyInt : Int;
      t_IndiceSuperior : Int;   // Indice superior del array
      t_IndiceInferior : Int;   // Indice inferior del array
      t_InformeVacio : "UDT_ZC_INF_PROD_MEM";
   END_VAR

   VAR CONSTANT 
      ACTUAL : Int := 1;
   END_VAR


BEGIN
	REGION DESCRIPCION
	//=======================================================================
	// (C)Copyright ZeusControl 2023 - 2026
	//=======================================================================
	(*
	### Información del Sistema
	| Hardware Compatible | Entorno de Ingeniería |
	|---------------------|-----------------------|
	| PLC serie 1200/1500 | TIA Portal 18         |
	
	---
	### Descripción Funcional
	Bloque de funcion que implementa la gestión de los datos de informe de producción de una máquina de membranas, manteniendo un histórico de hasta `#N_REGISTROS` producciones con todos los datos relevantes del proceso (volúmenes, Brix, FCV, temperaturas, tiempos, destinos y producto) para su trazabilidad y consulta desde el sistema de supervisión.
	
	Funcionalidades principales:
	- Detección de flanco ascendente en las órdenes `#OrdenInicioInforme` y `#OrdenFinInforme` mediante las variables estáticas `#s_P_Trig_Inicio` y `#s_P_Trig_Fin`, generando los pulsos internos `#s_OrdenInicioInforme` y `#s_OrdenFinInforme`.
	- Inicialización de informe: al recibir `#s_OrdenInicioInforme` se desplazan los informes existentes una posición hacia abajo mediante `MOVE_BLK_VARIANT`, se reinicializan los campos del nuevo informe activo (`#Informe[#ACTUAL]`: fechas, tiempo de producción, destinos, producto, litros, Brix, FCV y temperaturas) y se almacena la fecha/hora de inicio mediante la conversión realizada por `FC15005_ZC_DTL_TO_ZC_FECHA`.
	- Datos en curso: mientras `#EnProduccion` está activo, se copian en `#Informe[#ACTUAL]` los valores de proceso en tiempo real: tiempo de producción, destinos de concentrado y permeado, código de producto, litros (entrada, concentrado y permeado por diferencia), Brix, FCV y las tres temperaturas (concentrado, permeado y entrada), aplicando `LREAL_TO_REAL` para adaptar la precisión al tipo de dato del UDT.
	- Fin de informe: al detectar `#s_OrdenFinInforme` se registra la fecha/hora de fin en `#Informe[#ACTUAL].FechaFin`.
	- Borrado masivo: la entrada `#s_Borrar` permite limpiar completamente todos los registros del array, inicializando fechas, tiempos, litros, Brix, FCV, temperatura de concentrado y código de producto a sus valores neutros. La propia variable de orden se autodesactiva tras la operación.
	
	> **Restricciones:** PLC serie 1200/1500.
	
	---
	### Dependencias Requeridas
	| Tipo | Elementos |
	|------|-----------|
	| FC   | `FC15005_ZC_DTL_TO_ZC_FECHA` |
	| FB   | - |
	| DB   | - |
	| UDT  | `UDT_ZC_CONTADOR_TIEMPO`, `UDT_ZC_INF_PROD_MEM` |
	    
	---
	### Historial de Cambios
	| Versión  | Fecha      | Técnico | Cambios |
	|----------|------------|---------|---------|
	| 01.00.00 | 24.05.2023 | (ABH)   | Primera versión. |
	| 01.00.01 | 16.02.2024 | (ABH)   | Cambios en datos de REAL a LREAL para coincidir con UDT. |
	| 01.00.02 | 23.05.2024 | (HCR)   | Se añaden temperaturas de entrada, destinos concentrado y destinos permeado. |
	| 01.00.03 | 03.07.2026 | (ABH)   | Cambio a SCL. |
	*)
	END_REGION DESCRIPCION
	
	
	//  ==========================================================================================================
	REGION LIMITES
	    
	    //  Calculo limites array de informes
	    #t_IndiceInferior := DINT_TO_INT(LOWER_BOUND(ARR := #Informe, DIM := 1));
	    #t_IndiceSuperior := DINT_TO_INT(UPPER_BOUND(ARR := #Informe, DIM := 1));
	    
	    //  Si no hay informes configurados, salimos inmediatamente.
	    IF #t_IndiceInferior <= 0 THEN
	        RETURN;
	    END_IF;
	    
	END_REGION LIMITES
	
	
	//  ==========================================================================================================
	REGION GESTION_ORDENES
	    
	    //  Orden de inicio
	    #s_OrdenInicioInforme := #OrdenInicioInforme AND NOT #s_oldInicio;
	    
	    //  Orden de fin
	    #s_OrdenFinInforme := #OrdenFinInforme AND NOT #s_oldFin;
	    
	    //  Estados anteriores
	    #s_oldInicio := #OrdenInicioInforme;
	    #s_oldFin := #OrdenFinInforme;
	    
	END_REGION GESTION_ORDENES
	
	
	//  ==========================================================================================================
	REGION INICIALIZACION
	    
	    //  Con flanco de inicio informe, desplazamos los registros, reinicializamos los datos y almacenamos la fecha y hora de inicio, todo en la poisicion 1
	    IF #s_OrdenInicioInforme THEN
	        
	        //  Desplazamiento de datos
	        #t_dummyInt := MOVE_BLK_VARIANT(SRC := #Informe, COUNT := INT_TO_UDINT(#t_IndiceSuperior - 1), SRC_INDEX := 0, DEST_INDEX := 1, DEST => #Informe);
	        
	        //  Reinicializacion de los datos
	        #Informe[#ACTUAL] := #t_InformeVacio;
	        
	        //  Guardado de fecha y hora actual
	        #Informe[#ACTUAL].FechaInicio := "FC15005_ZC_DTL_TO_ZC_FECHA"(FechaDTL := #FechaHoraActual);
	        
	    END_IF;
	    
	END_REGION INICIALIZACION
	
	
	//  ==========================================================================================================
	REGION DATOS_DURANTE_PRODUCCION
	    
	    //  Se guardan los datos durante la produccion
	    IF #EnProduccion THEN
	        
	        #Informe[#ACTUAL].TiempoProduccion.Hora := #TiempoProduccion.Hora;
	        #Informe[#ACTUAL].TiempoProduccion.Minuto := #TiempoProduccion.Minuto;
	        #Informe[#ACTUAL].TiempoProduccion.Segundo := #TiempoProduccion.Segundo;
	        
	        #Informe[#ACTUAL].DestinoConcentrado := #DestinoConcentrado;
	        #Informe[#ACTUAL].DestinoPermeado := #DestinoPermeado;
	        #Informe[#ACTUAL].Producto := #Producto;
	        
	        #Informe[#ACTUAL].LitrosEntrada := LREAL_TO_REAL(#LitrosEntrada);
	        #Informe[#ACTUAL].LitrosConcentrado := LREAL_TO_REAL(#LitrosConcentrado);
	        #Informe[#ACTUAL].LitrosPermeado := LREAL_TO_REAL(#LitrosEntrada - #LitrosConcentrado);
	        
	        #Informe[#ACTUAL].Brix := LREAL_TO_REAL(#Brix);
	        #Informe[#ACTUAL].FCV := #Fcv;
	        
	        #Informe[#ACTUAL].TempConcentrado := LREAL_TO_REAL(#TempConcentrado);
	        #Informe[#ACTUAL].TempPermeado := LREAL_TO_REAL(#TempPermeado);
	        #Informe[#ACTUAL].TempEntrada := LREAL_TO_REAL(#TempEntrada);
	        
	    END_IF;
	    
	END_REGION DATOS_DURANTE_PRODUCCION
	
	
	//  ==========================================================================================================
	REGION FIN_INFORME
	    
	    //  Se guardan los datos durante la produccion
	    IF #s_OrdenFinInforme THEN
	        
	        //  Guardado de fecha y hora actual
	        #Informe[#ACTUAL].FechaFin := "FC15005_ZC_DTL_TO_ZC_FECHA"(FechaDTL := #FechaHoraActual);
	        
	    END_IF;
	    
	END_REGION FIN_INFORME
	
	
	//  ==========================================================================================================
	REGION BORRADO_DATOS
	    
	    //  Borrado de todos los registros
	    IF #s_OrdenBorrarInforme THEN
	        IF #s_NumeroBorrado = -1 OR #s_NumeroBorrado = 0 THEN
	            
	            FOR #for_i := 1 TO #t_IndiceSuperior DO
	                #Informe[#for_i] := #t_InformeVacio;
	            END_FOR;
	            
	        ELSIF #s_NumeroBorrado >= 1 AND #s_NumeroBorrado < #t_IndiceSuperior THEN
	            
	            //  Borrado registro especifico
	            #Informe[#s_NumeroBorrado] := #t_InformeVacio;
	            
	            //  Desplazamiento de datos
	            #t_dummyInt := MOVE_BLK_VARIANT(SRC := #Informe,
	                                            COUNT := INT_TO_UDINT(#t_IndiceSuperior - #s_NumeroBorrado),
	                                            SRC_INDEX := #s_NumeroBorrado,
	                                            DEST_INDEX := #s_NumeroBorrado - 1,
	                                            DEST => #Informe);
	            
	            //  Borrado del ultimo registro
	            #Informe[#t_IndiceSuperior] := #t_InformeVacio;
	            
	        ELSIF #s_NumeroBorrado = #t_IndiceSuperior THEN
	            
	            //  Borrado de ultimo registro
	            #Informe[#s_NumeroBorrado] := #t_InformeVacio;
	            
	        END_IF;
	        
	        #s_OrdenBorrarInforme := FALSE;
	        
	    END_IF;
	    
	END_REGION BORRADO_DATOS
	
END_FUNCTION_BLOCK

