{
    S7_Author := "HCR";
    S7_BlockComment := "MLC_3sr";
    S7_BlockNumber := "15102";
    S7_BlockTitle := "MLC_qJ";
    S7_EditorMode := "SCL";
    S7_Family := "ZeusControl";
    S7_IECCheck := "TRUE";
    S7_Optimized := "TRUE";
    S7_Version := "1.0"
}
FUNCTION_BLOCK "FB15102_ZC_CALCULO_FCV_UF"
    VAR_INPUT
        { S7_MLC := "MLC_546" }
        Pulso1Seg : Bool;
        { S7_MLC := "MLC_5AL" }
        ActivarCalculosCaudal : Bool;
        { S7_MLC := "MLC_mA" }
        ActivarControlPresion : Bool;
        { S7_MLC := "MLC_eL" }
        Marcha : Bool;
        { S7_MLC := "MLC_t4" }
        EtapaRecircProd : Bool;
        { S7_MLC := "MLC_ri" }
        CaudalTotalPermeado : Real;
        { S7_MLC := "MLC_4XA" }
        SP_FCV : Real;
        { S7_MLC := "MLC_3s2" }
        SP_CaudalMaximoEntrada : Real;
        { S7_MLC := "MLC_32z" }
        SP_PorcientoPermeadoTotal : Real;
        { S7_MLC := "MLC_4dw" }
        SP_BajoCaudalPermeado : Real;
        { S7_MLC := "MLC_hZ" }
        SP_PresionInicial : Real;
        { S7_MLC := "MLC_3kE" }
        SP_IncrementosPresion : Real;
        { S7_MLC := "MLC_3WS" }
        SP_PresionMaxima : Real;
        { S7_MLC := "MLC_33v" }
        SP_TiempoMuestreoPresion : Int;
        { S7_MLC := "MLC_4xx" }
        SP_PorcentajeDecremento : Real;
        { S7_MLC := "MLC_3DC" }
        SP_CaudalMinConcentrado : Real;
        { S7_MLC := "MLC_Z8" }
        SP_TiempoDecrementos : Int;
        { S7_MLC := "MLC_ds" }
        BotonReset : Bool;
    END_VAR
    VAR_OUTPUT
        { S7_MLC := "MLC_3VM" }
        SP_CaudalTeoricoEntrada : Real;
        { S7_MLC := "MLC_Yw" }
        SP_CaudalPermeado : Real;
        { S7_MLC := "MLC_3Qd" }
        SP_Presion : Real;
        { S7_MLC := "MLC_Za" }
        ErrorDiferenciaPermeado : Bool;
        { S7_MLC := "MLC_3CS" }
        VisibilidadConsignasCaudal : Bool;
        { S7_MLC := "MLC_4by" }
        VisibilidadConsignasPresion : Bool;
        { S7_MLC := "MLC_3df" }
        VisibilidadConsignaCaudalDec : Bool;
        { S7_MLC := "MLC_4tj" }
        PermisoReset : Bool;
        { S7_MLC := "MLC_3Qx" }
        AjusteSP_CaudalConcentrado : Bool;
        { S7_MLC := "MLC_4iH" }
        MaxPresionAlcanzada : Bool;
    END_VAR
    VAR_IN_OUT
        { S7_MLC := "MLC_43k" }
        SP_CaudalConcentrado : Real;
        { S7_MLC := "MLC_46z" }
        SP_CaudalPermeadoTotal : Real;
        { S7_MLC := "MLC_4FE" }
        ForzarIncrementos : Bool;
    END_VAR
    VAR RETAIN
        { S7_MLC := "MLC_4CB" }
        s_SP_Presion : Real;
        { S7_MLC := "MLC_3py" }
        s_old_SP_CaudalConcentrado : Real;
        { S7_MLC := "MLC_4pQ" }
        s_old_SP_FCV : Real;
    END_VAR
    VAR
        { S7_MLC := "MLC_4rA" }
        s_DiferenciaCaudalPermeado : Real;
        { S7_MLC := "MLC_4u3" }
        s_BajoCaudalPermeado : Bool;
        { S7_MLC := "MLC_gA" }
        s_MaxPresionAlcanzada : Bool;
        { S7_MLC := "MLC_4iE" }
        s_SP_CaudalConcentrado : Real;
        { S7_Setpoint := "False" }
        s_TON : Array[0..1] of _.UDT_ZC_DISP_TON := [
            { S7_MLC := "MLC_3wk" }
        (),
            { S7_MLC := "MLC_3cc" }
        ()
        ];
    END_VAR
    VAR_TEMP
        { S7_MLC := "MLC_3cE" }
        t_CaudalTeoricoEntrada : Real;
    END_VAR
    VAR CONSTANT
        { S7_MLC := "MLC_3Nk" }
        LIM_MIN_CAUDAL_PERMEADO_TOTAL : Real;
        { S7_MLC := "MLC_53J" }
        LIM_MAX_CAUDAL_PERMEADO_TOTAL : Real := 100.0;
    END_VAR

    { S7_Language := "SCL" }
    NETWORK
        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 `FB15102_ZC_CALCULO_FCV_UF` implementa el cálculo de la consigna de FCV (factor de conversión volumétrica) para máquinas de membranas de ultrafiltración, a partir de las consignas de caudal de concentrado y de FCV. Gestiona además el control de presión por incrementos/decrementos para mantener el caudal de permeado total dentro de los objetivos del proceso.
        
        Funcionalidades principales:
        - Validación de entradas: se detecta error (`ErrorDiferenciaPermeado`) cuando la consigna `SP_PorcientoPermeadoTotal` está fuera del rango definido por `LIM_MIN_CAUDAL_PERMEADO_TOTAL` y `LIM_MAX_CAUDAL_PERMEADO_TOTAL`. En caso de error, el cálculo del caudal teórico de permeado total se realiza con un valor de seguridad del 1%.
        - Cálculo de caudales teóricos: a partir de `SP_CaudalConcentrado` y `SP_FCV` se obtiene el caudal teórico de entrada (`SP_CaudalTeoricoEntrada`) y, por diferencia, el caudal teórico de permeado (`SP_CaudalPermeado`). Si el caudal teórico de entrada supera el `SP_CaudalMaximoEntrada`, la consigna de concentrado se reduce automáticamente, activando el flag `AjusteSP_CaudalConcentrado` para señalizar el reescalado al exterior.
        - Cálculo de caudal de permeado total: `SP_CaudalPermeadoTotal` se obtiene aplicando el porcentaje de permeado total al caudal de permeado teórico.
        - Detección de bajo caudal de permeado: se calcula la diferencia entre el caudal teórico y el real (`CaudalTotalPermeado`). El flag `s_BajoCaudalPermeado` se activa cuando la diferencia es positiva pero inferior al umbral `SP_BajoCaudalPermeado`, o cuando el teórico es superior al real.
        - Control de presión por incrementos: mediante un temporizador (`FC2928_ZC_TON_INDV` con estructura `ZC_TON`) se incrementa progresivamente la consigna `s_SP_Presion` en pasos `SP_IncrementosPresion` cuando se detecta bajo caudal de permeado, hasta alcanzar `SP_PresionMaxima`. Se permite reset a `SP_PresionInicial` mediante la entrada `BotonReset`, así como el disparo manual de un incremento con `ForzarIncrementos`.
        - Lógica de decrementos de caudal: cuando se alcanza la presión máxima (`s_MaxPresionAlcanzada`) y el caudal real sigue por debajo del teórico, un segundo temporizador (`s_TON[1]`) reduce progresivamente la consigna de concentrado aplicando un porcentaje `SP_PorcentajeDecremento`, acotado inferiormente por `SP_CaudalMinConcentrado`. Cada ajuste activa `AjusteSP_CaudalConcentrado`.
        - Modo no activo: cuando `ActivarCalculosCaudal` o `ActivarControlPresion` están desactivados, todas las salidas y variables internas se resetean a sus valores neutros, manteniendo únicamente la presión inicial si el proceso no se encuentra en recirculación de producto (`EtapaRecircProd`).
        - Salidas de visibilidad HMI: `VisibilidadConsignasCaudal`, `VisibilidadConsignasPresion` y `VisibilidadConsignaCaudalDec` se exponen para que el sistema de supervisión muestre u oculte las pantallas de consignas según la etapa activa del bloque.
        - Memoria de consignas anteriores: al final del bloque se actualizan `s_old_SP_CaudalConcentrado` y `s_old_SP_FCV` para la detección de cambios en el siguiente ciclo.
        
        > **Restricciones:** Ninguna.
        
        ---
        ### Dependencias Requeridas
        | Tipo | Elementos |
        |------|-----------|
        | FC   | `FC2928_ZC_TON_INDV` |
        | FB   | - |
        | DB   | - |
        | UDT  | `ZC_TON` |
            
        ---
        ### Historial de Cambios
        | Versión  | Fecha      | Técnico | Cambios |
        |----------|------------|---------|---------|
        | 01.00.00 | 24.05.2023 | (HCR)   | Primera versión. |
        | 01.00.01 | 04.03.2024 | (HCR)   | Se corrige gestión de `SP_Presion`. Se añade variable estática para su gestión. |
        | 01.00.02 | 10.11.2025 | (ABH)   | Se añade salida `AjusteSP_CaudalConcentrado` para poder realizar escritura de parámetros fuera del bloque. |
        | 01.00.03 | 20.11.2025 | (CNR)   | Se añade lógica de funcionamiento por decrementos. |
        | 01.00.04 | 28.11.2025 | (ABH)   | Se elimina la parte de la diafiltración. Se añade gestión con temporizadores ZC. Se eliminan variables externas al bloque. Se estandariza el nombre de variables. |
        *)
        END_REGION DESCRIPCION
        
        REGION ERROR
            
            //  ====================================================
            //  SP_PorcientoPermeadoTotal fuera de rango
            #ErrorDiferenciaPermeado := (#SP_PorcientoPermeadoTotal < #LIM_MIN_CAUDAL_PERMEADO_TOTAL OR #SP_PorcientoPermeadoTotal > #LIM_MAX_CAUDAL_PERMEADO_TOTAL);
            
        END_REGION
        
        
        REGION CALCULO_FCV
            
            //  Reinicializacion de valores
            #AjusteSP_CaudalConcentrado := FALSE;
            
            //  ====================================================
            //  Modo activo
            IF #ActivarCalculosCaudal THEN
                
                #s_SP_CaudalConcentrado := #SP_CaudalConcentrado;
                
                //  ====================================================
                //  Cambio de consigna caudal de concentrado
                IF #s_SP_CaudalConcentrado <> #s_old_SP_CaudalConcentrado OR #SP_FCV <> #s_old_SP_FCV THEN
                    //  Cálculo temporal de caudal teórico de entrada
                    #t_CaudalTeoricoEntrada := #s_SP_CaudalConcentrado * #SP_FCV;
                    //  Comparación con el caudal máximo de entrada
                    IF #t_CaudalTeoricoEntrada > #SP_CaudalMaximoEntrada THEN
                        //  Recalcular caudal de concentrado
                        #s_SP_CaudalConcentrado := #SP_CaudalMaximoEntrada / #SP_FCV;
                        #AjusteSP_CaudalConcentrado := TRUE;
                    END_IF;
                    
                END_IF;
                
                //  Cálculo caudal teórico de entrada
                #SP_CaudalTeoricoEntrada := #s_SP_CaudalConcentrado * #SP_FCV;
                //  Cálculo caudal teórico de permeado
                #SP_CaudalPermeado := #SP_CaudalTeoricoEntrada - #s_SP_CaudalConcentrado;
                
                //  Cálculo caudal teórico de permeado total
                IF NOT #ErrorDiferenciaPermeado THEN
                    #SP_CaudalPermeadoTotal := #SP_CaudalPermeado * ((#SP_PorcientoPermeadoTotal / 100) + 1);
                ELSE
                    #SP_CaudalPermeadoTotal := #SP_CaudalPermeado * ((1 / 100) + 1);
                END_IF;
                
                //  Comprobación bajo caudal de permeado total para incremento de presión
                IF #SP_CaudalPermeadoTotal > #CaudalTotalPermeado THEN
                    #s_DiferenciaCaudalPermeado := #SP_CaudalPermeadoTotal - #CaudalTotalPermeado;
                ELSE
                    #s_DiferenciaCaudalPermeado := 0.0;
                END_IF;
                
                #s_BajoCaudalPermeado := (#s_DiferenciaCaudalPermeado < #SP_BajoCaudalPermeado AND #s_DiferenciaCaudalPermeado > 0.0 ) OR (#SP_CaudalPermeadoTotal > #CaudalTotalPermeado);
                
            ELSE
                
                //  ====================================================
                //  Modo no activo
                #SP_CaudalTeoricoEntrada := 0.0;
                #SP_CaudalPermeado := 0.0;
                #SP_CaudalPermeadoTotal := 0.0;
                #s_DiferenciaCaudalPermeado := 0.0;
                #s_BajoCaudalPermeado := FALSE;
                
                #s_MaxPresionAlcanzada := FALSE;
                
            END_IF;
            
            
        END_REGION
        
        
        REGION PRESION 
            
            REGION TEMPORIZADOR
                
                //  ====================================================
                //  Temporizador para incrementos de presión
                //  Configuración temporizador
                #s_TON[0].SP := #SP_TiempoMuestreoPresion;
                #s_TON[0].IN := NOT #s_TON[0].Q AND #ActivarControlPresion AND #Marcha AND #s_BajoCaudalPermeado AND NOT #BotonReset AND (#s_SP_Presion < #SP_PresionMaxima);
                
                "FC2928_ZC_TON_INDV"(Pulso1seg:=#Pulso1Seg,
                                     TON:=#s_TON[0]);
                
            END_REGION
            
            IF #ActivarControlPresion THEN
                
                #PermisoReset := TRUE;
                
                //  ====================================================
                //  Incrementos de presión
                IF #s_TON[0].Q OR #ForzarIncrementos AND (#s_SP_Presion < #SP_PresionMaxima) THEN
                    #ForzarIncrementos := FALSE;
                    #s_SP_Presion += #SP_IncrementosPresion;
                    //  Si el valor de presión calculado es mayor a la presión máxima, movemos la consigna máxima de presión
                    IF #s_SP_Presion > #SP_PresionMaxima THEN
                        #s_SP_Presion := #SP_PresionMaxima;
                    END_IF;
                END_IF;
                
                //  Reset valor de presión a presión inicial
                IF #BotonReset THEN
                    #s_SP_Presion := #SP_PresionInicial;
                END_IF;
                
            ELSE
        
                IF NOT #EtapaRecircProd THEN
                    #s_SP_Presion := #SP_PresionInicial;
                END_IF;
                
                #ForzarIncrementos := FALSE;
                #PermisoReset := FALSE;
            END_IF;
            
            //  ====================================================
            //  Limite presion
            IF #s_SP_Presion < #SP_PresionInicial THEN
                #s_SP_Presion := #SP_PresionInicial;
            END_IF;
            
        END_REGION
        
        
        REGION DECREMENTOS_CAUDAL
            
            REGION TEMPORIZADOR
                
                //  ====================================================
                //  Temporizador para decrementos de caudal
                // Configuración temporizador
                #s_TON[1].SP := #SP_TiempoDecrementos;
                #s_TON[1].IN := NOT #s_TON[1].Q AND #ActivarControlPresion AND #Marcha AND #s_MaxPresionAlcanzada AND (#CaudalTotalPermeado < #SP_CaudalPermeadoTotal);
                
                "FC2928_ZC_TON_INDV"(Pulso1seg:=#Pulso1Seg,
                                     TON:=#s_TON[1]);
                
            END_REGION
            
            IF #ActivarControlPresion THEN
                
                //  Presion máxima alcanzada
                IF #s_SP_Presion >= #SP_PresionMaxima THEN
                    #s_MaxPresionAlcanzada := TRUE;
                ELSE
                    #s_MaxPresionAlcanzada := FALSE;
                END_IF;
            
                //  ====================================================
                //  Decrementos de caudal
                IF #s_TON[1].Q AND (#s_SP_CaudalConcentrado > #SP_CaudalMinConcentrado) THEN
                    #s_SP_CaudalConcentrado := #s_SP_CaudalConcentrado * (1 - (#SP_PorcentajeDecremento / 100.0));
                    
                    //  Si el valor de caudal calculado es menor al caudal minimo, movemos la consigna minima de caudal
                    IF #s_SP_CaudalConcentrado < #SP_CaudalMinConcentrado THEN
                        #s_SP_CaudalConcentrado := #SP_CaudalMinConcentrado;
                    END_IF;
                    #AjusteSP_CaudalConcentrado := TRUE;
                END_IF;
                
            END_IF;
            
        END_REGION
        
        
        REGION SALIDAS
            
            //  ====================================================
            //  Salida de presion
            #SP_Presion := #s_SP_Presion;
            
            //  ====================================================
            //  Maxima presion alcanzada
            #MaxPresionAlcanzada := #s_MaxPresionAlcanzada;
            
            //  ====================================================
            //  Visibilidad en HMI
            #VisibilidadConsignasCaudal := #ActivarCalculosCaudal;
            #VisibilidadConsignasPresion := #ActivarControlPresion;
            #VisibilidadConsignaCaudalDec := #ActivarControlPresion AND #s_MaxPresionAlcanzada;
            
            #SP_CaudalConcentrado := #s_SP_CaudalConcentrado;
            
        END_REGION ;
        
        
        //  Estados anteriores
        #s_old_SP_CaudalConcentrado := #s_SP_CaudalConcentrado;
        #s_old_SP_FCV := #SP_FCV;
        
    END_NETWORK
END_FUNCTION_BLOCK
