{
    S7_Author := "HCR";
    S7_BlockComment := "MLC_424";
    S7_BlockNumber := "15056";
    S7_BlockTitle := "MLC_wi";
    S7_EditorMode := "SCL";
    S7_Family := "ZeusControl";
    S7_IECCheck := "TRUE";
    S7_Optimized := "TRUE";
    S7_Version := "1.0"
}
FUNCTION_BLOCK "FB15056_ZC_SIVE_MEMBRANA_CARE"
    VAR_INPUT
        { S7_MLC := "MLC_4dN" }
        Pulso1Seg : Bool;
        { S7_MLC := "MLC_aA" }
        OrdenRegistroGeneral : Bool;
        { S7_MLC := "MLC_4R6" }
        FechaHora : DTL;
        { S7_MLC := "MLC_4aC" }
        Iniciar : Bool;
    END_VAR
    VAR_INPUT RETAIN
        { S7_MLC := "MLC_iL" }
        Usuario : String[25];
    END_VAR
    VAR_INPUT
        { S7_MLC := "MLC_ah" }
        TecnicoRegistroGeneral : String[20];
    END_VAR
    VAR_INPUT RETAIN
        { S7_MLC := "MLC_R3" }
        TiempoAvisoOn : Int;
        { S7_MLC := "MLC_3Xs" }
        TiempoAvisoOff : Int;
        { S7_MLC := "MLC_4fd" }
        TiempoAlarmaOn : Int;
        { S7_MLC := "MLC_37r" }
        TiempoAlarmaOff : Int;
        { S7_MLC := "MLC_3gf" }
        HorasAviso : Int;
        { S7_MLC := "MLC_3Li" }
        HorasAlarma : Int;
        { S7_MLC := "MLC_4Lh" }
        HorasAlarmaCritica : Int;
    END_VAR
    VAR_OUTPUT
        { S7_MLC := "MLC_4m7" }
        Aviso : Bool;
        { S7_MLC := "MLC_4iM" }
        Alarma : Bool;
        { S7_MLC := "MLC_4pX" }
        AlarmaCritica : Bool;
        { S7_MLC := "MLC_3Yx" }
        Estado : Int;
        { S7_MLC := "MLC_3Uj" }
        TiempoMembranaRestante : UDInt;
    END_VAR
    VAR_IN_OUT
        { S7_MLC := "MLC_4f4" }
        OrdenBorrar : Bool;
    END_VAR
    VAR RETAIN
        { S7_Setpoint := "False" }
        Registros : Array[1..#N_REGISTROS] of _.UDT_ZC_SIVE_MEMBRANA_CARE;
    END_VAR
    VAR
        {
            S7_MLC := "MLC_4vB";
            S7_Setpoint := "False"
        }
        Pulsador : _.UDT_ZC_PULSADOR;
    END_VAR
    VAR RETAIN
        {
            S7_MLC := "MLC_qZ";
            S7_Setpoint := "False"
        }
        Tiempo : _.UDT_ZC_CONTADOR_TIEMPO;
        { S7_MLC := "MLC_42M" }
        s_PuestaMarchaAno : UInt;
        { S7_MLC := "MLC_4G6" }
        s_PuestaMarchaMes : Int;
        { S7_MLC := "MLC_3pA" }
        s_PuestaMarchaDia : Int;
        { S7_MLC := "MLC_4bJ" }
        s_Indice : Int := 1;
        { S7_MLC := "MLC_cp" }
        s_TecnicoNuevoRegistro : String[20];
        { S7_Setpoint := "False" }
        s_FlipFlopAviso : _.UDT_ZC_DISP_FF;
        { S7_Setpoint := "False" }
        s_FlipFlopAlarma : _.UDT_ZC_DISP_FF;
    END_VAR
    VAR_TEMP
        { S7_MLC := "MLC_4q5" }
        for_i : Int;
        { S7_MLC := "MLC_3Tb" }
        t_OrdenReset : Bool;
    END_VAR
    VAR CONSTANT
        { S7_MLC := "MLC_yV" }
        N_REGISTROS : Int := 10;
    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
        El bloque `FB15056_ZC_REGISTRO_MEMBRANAS` implementa la gestión integral del registro y vida útil de las membranas. Mantiene un histórico circular de intervenciones (`Registros[i]`), controla el tiempo de uso acumulado y genera los avisos, alarmas y alarma crítica en función de las horas restantes, incluyendo la posibilidad de registrar cambios generales de forma masiva.
        
        Funcionalidades principales:
        - Registro de intervención por pulsador: el bloque gestiona un pulsador (`FC15026_ZC_PULSADOR`) con permiso dependiente de que haya un técnico introducido en `#s_TecnicoNuevoRegistro`. Al confirmarse la pulsación (`Pulsador.Plc`), se almacena en `#Registros[#s_Indice]` la fecha/hora convertida con `FC15005_ZC_DTL_TO_ZC_FECHA`, el técnico y el tiempo de membrana (`#Tiempo.Hora`), se limpia el técnico y se dispara la orden interna `#t_OrdenReset` para reiniciar el contador de tiempo. El índice avanza de forma circular hasta `#N_REGISTROS`.
        - Registro general: la entrada `#OrdenRegistroGeneral` permite registrar un cambio de membrana sin pulsador físico, asociando el técnico definido en `#TecnicoRegistroGeneral` y siguiendo la misma lógica circular de índice.
        - Contador de tiempo de membrana: mediante `FC15025_ZC_CONTADOR_TIEMPO` se acumula el tiempo de uso en horas, con posibilidad de reset a través de `#t_OrdenReset`.
        - Cálculo de horas restantes: `#TiempoMembranaRestante` se calcula como la diferencia entre `#HorasAlarmaCritica` y `#Tiempo.Hora`, acotado a `0` para evitar desbordamientos cuando se supera el umbral crítico.
        - Borrado de registros: la entrada `#OrdenBorrar` limpia completamente el array de registros (fecha, hora, técnico y tiempo de membrana) y reinicia el índice de escritura a `1`.
        - Estado global por tramos: clasificación del estado en `#Estado` (0: Normal, 1: Aviso, 2: Alarma, 3: Alarma crítica) según el rango de horas acumulado frente a `#HorasAviso`, `#HorasAlarma` y `#HorasAlarmaCritica`.
        - Aviso y Alarma intermitentes: se generan mediante dos Flip-Flop (`FC2926_ZC_FF_INDV`) con tiempos de encendido/apagado configurables (`#TiempoAvisoOn/Off`, `#TiempoAlarmaOn/Off`) para producir señales parpadeantes. La alarma crítica (`AlarmaCritica`) es continua y se activa definitivamente al alcanzar `#HorasAlarmaCritica`.
        
        > **Restricciones:** Ninguna.
        
        ---
        ### Dependencias Requeridas
        | Tipo | Elementos |
        |------|-----------|
        | FC   | `FC15026_ZC_PULSADOR`, `FC15025_ZC_CONTADOR_TIEMPO`, `FC2926_ZC_FF_INDV`, `FC15005_ZC_DTL_TO_ZC_FECHA` |
        | FB   | - |
        | DB   | - |
        | UDT  | `ZC_FECHA`, `UDT_ZC_PULSADOR`, `UDT_ZC_CONTADOR_TIEMPO`. `UDT_ZC_SIVE_MEMBRANA_CARE` |
            
        ---
        ### Historial de Cambios
        | Versión  | Fecha      | Técnico | Cambios |
        |----------|------------|---------|---------|
        | 01.00.00 | 10.05.2023 | (HCR)   | Primera versión. |
        | 01.00.01 | 02.10.2023 | (ABH)   | Cambio nombre a SIVE MEMBRANA CARE. Se corrigen permisos variables estáticas. Se corrigen nombres de variables. Se crea UDT `registro membranas`. Se cambia tipo de dato fecha/hora string a `ZC_FECHA`. Se quitan temporizadores y se utiliza Flip-Flop individual. Se eliminan variables innecesarias. |
        | 01.00.02 | 16.01.2024 | (ABH)   | Se limita el tiempo restante de las membranas para que no se desborde. |
        | 01.00.03 | 02.12.2025 | (ABH)   | Se corrige cálculo tiempo horas restantes. |
        | 01.00.04 | 10.01.2026 | (CNR)   | Se añade orden y gestión de registro general. |
        *)
        END_REGION DESCRIPCION
        
        
        //  =============================================================================
        REGION ORDEN_REGISTRO
            
            #Pulsador.Permiso := (#s_TecnicoNuevoRegistro <> '');
            "FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
                                  SP_Tiempo := 2,
                                  TrazaCategoria:=0,
                                  TrazaIdProceso:=0,
                                  TrazaIdPulsador:=0,
                                  Usuario := #Usuario,
                                  FechaHoraActual := #FechaHora,
                                  Pulsador := #Pulsador);
            
            //  Orden pulsador
            IF #Pulsador.Plc THEN
                
                #Registros[#s_Indice].FechaHora := "FC15005_ZC_DTL_TO_ZC_FECHA"( #FechaHora);
                #Registros[#s_Indice].Tecnico := #s_TecnicoNuevoRegistro;
                #Registros[#s_Indice].TiempoMembrana := #Tiempo.Hora;
                
                //  Reset valor técnico y orden reset tiempo
                #s_TecnicoNuevoRegistro := '';
                
                #t_OrdenReset := TRUE;
                
                //  Incrementamos indice y si es mayor que el número máximo de registro lo ponemos en la posición inicial
                #s_Indice += 1;
                IF #s_Indice > #N_REGISTROS THEN
                    #s_Indice := 1;
                END_IF;
            END_IF;
            
            //  Orden registro general
            IF #OrdenRegistroGeneral THEN
                
                #Registros[#s_Indice].FechaHora := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaHora);
                #Registros[#s_Indice].Tecnico := #TecnicoRegistroGeneral;
                #Registros[#s_Indice].TiempoMembrana := #Tiempo.Hora;
                
                //  Reset valor técnico y orden reset tiempo
                #t_OrdenReset := TRUE;
                
                //  Incrementamos indice y si es mayor que el número máximo de registro lo ponemos en la posición inicial
                #s_Indice += 1;
                IF #s_Indice > #N_REGISTROS THEN
                    #s_Indice := 1;
                END_IF;
            END_IF;
            
        END_REGION ORDEN_REGISTRO
        
        
        // =============================================================================
        REGION TIEMPO_MEMBRANAS
            
            //  Contador de tiempo
            "FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
                                         Iniciar := #Iniciar,
                                         Contador := #Tiempo,
                                         Reset := #t_OrdenReset);
            
            //  Horas Restante
            IF INT_TO_UDINT(#HorasAlarmaCritica) > #Tiempo.Hora THEN
                #TiempoMembranaRestante := INT_TO_UDINT(#HorasAlarmaCritica) - #Tiempo.Hora;
            ELSE
                #TiempoMembranaRestante := 0;
            END_IF;
            
        END_REGION TIEMPO_MEMBRANAS
        
        
        //  =============================================================================
        REGION BORRAR_REGISTROS
            
            IF #OrdenBorrar THEN
                
                FOR #for_i := 1 TO #N_REGISTROS DO
                    
                    #Registros[#for_i].FechaHora.Ano := 0;
                    #Registros[#for_i].FechaHora.Mes := 0;
                    #Registros[#for_i].FechaHora.Dia := 0;
                    #Registros[#for_i].FechaHora.Hora := 0;
                    #Registros[#for_i].FechaHora.Minuto := 0;
                    #Registros[#for_i].FechaHora.Segundo := 0;
                    #Registros[#for_i].FechaHora.NanoSegundo := 0;
                    
                    #Registros[#for_i].Tecnico := '';
                    #Registros[#for_i].TiempoMembrana := 0;
                    #s_Indice := 1;
                    
                END_FOR;
                
                #OrdenBorrar := FALSE;
            END_IF;
            
        END_REGION BORRAR_REGISTROS
        
        
        //  =============================================================================
        REGION ESTADO
            
            #s_FlipFlopAviso.Orden_Activar := FALSE;
            #s_FlipFlopAviso.Config_SetPointTiempoFlip := #TiempoAvisoOn;
            #s_FlipFlopAviso.Config_SetPointTiempoFlop := #TiempoAvisoOff;
            #s_FlipFlopAlarma.Orden_Activar := FALSE;
            #s_FlipFlopAlarma.Config_SetPointTiempoFlip := #TiempoAlarmaOn;
            #s_FlipFlopAlarma.Config_SetPointTiempoFlop := #TiempoAlarmaOff;
            
            //  Estado Aviso
            #Estado := 0;
            
            IF #Tiempo.Hora >= #HorasAviso AND #Tiempo.Hora < #HorasAlarma THEN
                #s_FlipFlopAviso.Orden_Activar := TRUE;
                #Aviso := #s_FlipFlopAviso.#Estado_Flip;
                #Estado := 1;
            ELSE
                #Aviso := FALSE;
            END_IF;
            
            
            //  Estado Alarma
            IF #Tiempo.Hora >= #HorasAlarma AND #Tiempo.Hora < #HorasAlarmaCritica THEN
                #s_FlipFlopAlarma.Orden_Activar := TRUE;
                #Alarma := #s_FlipFlopAlarma.#Estado_Flip;
                #Estado := 2;
            ELSE
                #Alarma := FALSE;
            END_IF;
            
            
            //  Estado Alarma Crítica
            IF #Tiempo.Hora >= #HorasAlarmaCritica THEN
                #AlarmaCritica := TRUE;
                #Estado := 3;
            ELSE
                #AlarmaCritica := FALSE;
            END_IF;
            
        END_REGION ESTADO
        
        
        //  =============================================================================
        REGION GESTION_FF
            
            //  =============================================================================
            //  Flip Flop aviso
            "FC2926_ZC_FF_INDV"(Pulso1Seg := #Pulso1Seg,
                         FF := #s_FlipFlopAviso);
            
            
            //  =============================================================================
            //  Flip Flop alarma
            "FC2926_ZC_FF_INDV"(Pulso1Seg := #Pulso1Seg,
                         FF := #s_FlipFlopAlarma);
            
           
        END_REGION GESTION_FF
        
        
    END_NETWORK
END_FUNCTION_BLOCK
