{
    S7_Author := "ABH";
    S7_BlockComment := "MLC_4xt";
    S7_BlockNumber := "15052";
    S7_BlockTitle := "MLC_38Z";
    S7_EditorMode := "SCL";
    S7_Family := "ZeusControl";
    S7_IECCheck := "TRUE";
    S7_Optimized := "TRUE";
    S7_Version := "1.0"
}
FUNCTION_BLOCK "FB15052_ZC_INF_PROD_MEM"
    VAR_INPUT
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_T3";
            S7_Visibility := "Hidden := External"
        }
        OrdenInicioInforme : Bool;
        { S7_MLC := "MLC_QW" }
        OrdenFinInforme : Bool;
        { S7_MLC := "MLC_f7" }
        EnProduccion : Bool;
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_3hX";
            S7_Visibility := "Hidden := External"
        }
        FechaHoraActual : DTL;
        { S7_MLC := "MLC_3kT" }
        Producto : Int;
        { S7_MLC := "MLC_Y4" }
        DestinoConcentrado : Int;
        { S7_MLC := "MLC_4tF" }
        DestinoPermeado : Int;
        { S7_MLC := "MLC_3cZ" }
        LitrosEntrada : LReal;
        { S7_MLC := "MLC_3sg" }
        LitrosConcentrado : LReal;
        { S7_MLC := "MLC_3E4" }
        Brix : LReal;
        { S7_MLC := "MLC_3fm" }
        Fcv : Real;
        { S7_MLC := "MLC_4hp" }
        TempEntrada : LReal;
        { S7_MLC := "MLC_43h" }
        TempConcentrado : LReal;
        { S7_MLC := "MLC_f4" }
        TempPermeado : LReal;
        { S7_MLC := "MLC_zG" }
        TiempoProduccion : _.UDT_ZC_CONTADOR_TIEMPO;
    END_VAR
    VAR_IN_OUT
        Informe : Array[*] of _.UDT_ZC_INF_PROD_MEM;
    END_VAR
    VAR
        {
            S7_MLC := "MLC_4Xi";
            S7_Setpoint := "True"
        }
        s_OrdenInicioInforme : Bool;
        {
            S7_MLC := "MLC_4Ms";
            S7_Setpoint := "True"
        }
        s_OrdenFinInforme : Bool;
    END_VAR
    VAR RETAIN
        {
            S7_MLC := "MLC_4Uk";
            S7_Setpoint := "True"
        }
        s_oldInicio : Bool;
        {
            S7_MLC := "MLC_4da";
            S7_Setpoint := "True"
        }
        s_oldFin : Bool;
    END_VAR
    VAR
        { S7_MLC := "MLC_3qD" }
        s_OrdenBorrarInforme : Bool;
        { S7_MLC := "MLC_4Wm" }
        s_NumeroBorrado : Int;
    END_VAR
    VAR_TEMP
        for_i : Int;
        t_dummyInt : Int;
        { S7_MLC := "MLC_dm" }
        t_IndiceSuperior : Int;
        { S7_MLC := "MLC_jR" }
        t_IndiceInferior : Int;
        t_InformeVacio : _.UDT_ZC_INF_PROD_MEM;
    END_VAR
    VAR CONSTANT
        ACTUAL : Int := 1;
    END_VAR

    { S7_Language := "SCL" }
    NETWORK
        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_NETWORK
END_FUNCTION_BLOCK
