{
    S7_Author := "ABH";
    S7_BlockComment := "MLC_4W6";
    S7_BlockNumber := "15152";
    S7_BlockTitle := "MLC_dd";
    S7_EditorMode := "SCL";
    S7_Family := "ZeusControl";
    S7_IECCheck := "TRUE";
    S7_Optimized := "TRUE";
    S7_Version := "1.0"
}
FUNCTION_BLOCK "FB15152_ZC_PARO_SEC"
    VAR_INPUT
        { S7_MLC := "MLC_4Y8" }
        Pulso1Seg : Bool;
        { S7_MLC := "MLC_44T" }
        MarchaSecuenciaParo : Bool;
        { S7_MLC := "MLC_Rc" }
        NumeroDispositivos : Int := 4;
        { S7_MLC := "MLC_3E7" }
        TiempoEntreEtapas : Int := 2;
        { S7_MLC := "MLC_4QU" }
        ConfirmacionDisp : Array[1..#N_MAX] of Bool;
    END_VAR
    VAR_OUTPUT
        { S7_MLC := "MLC_38V" }
        SecParoFinalizada : Bool;
        { S7_MLC := "MLC_3u8" }
        SecReposo : Bool;
        { S7_MLC := "MLC_4WT" }
        ParoDispositivo : Array[1..#N_MAX] of Bool;
    END_VAR
    VAR
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_4U6";
            S7_Visibility := "Hidden := External"
        }
        s_NumeroDispositivos : Int;
    END_VAR
    VAR RETAIN
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_4xY";
            S7_Visibility := "Hidden := External"
        }
        s_EtapaActual : Int;
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_3pH";
            S7_Visibility := "Hidden := External"
        }
        s_Indice : Int;
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_37E";
            S7_Visibility := "Hidden := External"
        }
        s_TiempoActual : Int;
    END_VAR
    VAR_TEMP
        for_i : Int;
    END_VAR
    VAR CONSTANT
        N_MAX : Int := 8;
        REPOSO : Int := 0;
        PARANDO : Int := 10;
        REARMANDO : Int := 20;
        FINALIZADO : Int := 30;
    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 `FB15051_PARO_SECUENCIAL` implementa un secuenciador bidireccional para el paro ordenado de un array de dispositivos. Permite detener los dispositivos uno a uno esperando una confirmación por etapa, y rearmar la secuencia en sentido inverso si se retira la orden de marcha, continuando desde la última etapa alcanzada en lugar de reiniciar desde el inicio.
        
        Funcionalidades principales:
        - Validación del número de dispositivos: `#NumeroDispositivos` se acota entre 1 y la constante de dimensionamiento `#N_MAX`.
        - Estado `REPOSO`: bloque en espera. Al recibir `#MarchaSecuenciaParo := TRUE` se inicializa el índice y se transita a la etapa `PARANDO`.
        - Estado `PARANDO`: avanza secuencialmente por los dispositivos de menor a mayor índice. Cada dispositivo se considera parado cuando se cumple `#ConfirmacionDisp[i] = TRUE` durante `#TiempoEntreEtapas` segundos. Al confirmar, se incrementa el índice hasta superar `#s_NumeroDispositivos`, momento en el que se transita a `FINALIZADO`. Si durante esta etapa se desactiva `#MarchaSecuenciaParo`, se invierte el sentido y se pasa a `REARMANDO` sin perder la posición alcanzada.
        - Estado `REARMANDO`: retrocede secuencialmente por los dispositivos de mayor a menor índice. Cada dispositivo se considera rearmado cuando `#ConfirmacionDisp[i] = FALSE` durante `#TiempoEntreEtapas` segundos. Al rearmar, se decrementa el índice hasta alcanzar 1, momento en el que se transita a `REPOSO`. Si durante esta etapa se vuelve a activar `#MarchaSecuenciaParo`, se invierte nuevamente el sentido y se pasa a `PARANDO` desde la posición actual.
        - Estado `FINALIZADO`: secuencia de paro completa. Al retirar `#MarchaSecuenciaParo`, se rearman los dispositivos en orden inverso empezando desde el último, transitando a `REARMANDO`.
        - Mapeado dinámico de salidas: para cada dispositivo se activa `#ParoDispositivo[i] := TRUE` si su índice es menor o igual al puntero actual y la etapa es `PARANDO`; en el resto de casos se mantiene en `FALSE`. Esto garantiza que los dispositivos ya parados en la fase actual permanezcan parados durante el rearmado inverso de los posteriores.
        - Señales de estado: `#SecReposo` indica que la secuencia está en `REPOSO` y `#SecParoFinalizada` que ha terminado (`FINALIZADO`).
        - Contadores de tiempo por etapa del tipo `ZC_TIEMPO_ETAPA` con base de tiempo en segundos (`#Pulso1Seg`).
        
        > **Restricciones:** PLC serie 1200/1500.
        
        ---
        ### Dependencias Requeridas
        | Tipo | Elementos |
        |------|-----------|
        | FC   | - |
        | FB   | - |
        | DB   | - |
        | UDT  | `ZC_TIEMPO_ETAPA` |
        
        ---
        ### Historial de Cambios
        | Versión  | Fecha      | Técnico | Cambios |
        |----------|------------|---------|---------|
        | 00.00.01 | 24.05.2023 | (ABH)   | Primera versión. |
        | 00.00.02 | 15.12.2023 | (HCR)   | Se aumentan los dispositivos de 4 a 8. |
        | 00.00.03 | 18.12.2023 | (ABH)   | Se cambia UDT tiempo etapa a `ZC_TIEMPO_ETAPA`. Se añade salida `SecReposo` para indicar que está en reposo. Se añade gestión de etapas para que, en caso de estar con orden de paro y se dé marcha, siga la secuencia donde le toque evitando tener que pararla por completo y volverla a arrancar. |
        | 01.00.00 | 03.07.2026 | (ABH)   | Cambio a SCL. Se refactoriza logicas para estandarizar. Se utiliza constante N_MAX para redimensionar. |
        *)
        END_REGION DESCRIPCION
        
        
        // ==========================================================================================================
        REGION LIMITES
            
            #s_NumeroDispositivos := #NumeroDispositivos;
            
            // Limite de seguridad
            IF #s_NumeroDispositivos > #N_MAX THEN
                #s_NumeroDispositivos := #N_MAX;
            ELSIF #s_NumeroDispositivos < 1 THEN
                #s_NumeroDispositivos := 1;
            END_IF;
            
        END_REGION LIMITES
        
        
        // ==========================================================================================================
        REGION SECUENCIADOR_BIDIRECCIONAL
            
            CASE #s_EtapaActual OF
                    
                #REPOSO:
                    #s_Indice := 1;
                    #s_TiempoActual := 0;
                    
                    IF #MarchaSecuenciaParo THEN
                        #s_EtapaActual := #PARANDO;
                    END_IF;
                    
                #PARANDO:
                    
                    //  Contador de tiempo con confirmacion de dispositivo
                    IF #ConfirmacionDisp[#s_Indice] AND #Pulso1Seg THEN
                        #s_TiempoActual += 1;
                    END_IF;
                    
                    //  Tiempo cumplido
                    IF (#s_TiempoActual >= #TiempoEntreEtapas) THEN
                        #s_TiempoActual := 0;
                        #s_Indice += 1; //  Avanzamos al siguiente dispositivo
                        
                        //  Comprobación de fin de secuencia de paro
                        IF #s_Indice > #s_NumeroDispositivos THEN
                            #s_EtapaActual := #FINALIZADO;
                        END_IF;
                    END_IF;
                    
                    //  Si se desactiva la orden de marcha de la secuencia, comenzamos el rearme inverso
                    IF NOT #MarchaSecuenciaParo THEN
                        #s_TiempoActual := 0;
                        #s_EtapaActual := #REARMANDO;
                    END_IF;
                    
                    
                #REARMANDO:
                    
                    //  Límite de seguridad inferior
                    IF #s_Indice < 1 THEN
                        #s_EtapaActual := #REPOSO;
                        RETURN;
                    END_IF;
                    
                    //  Contador de tiempo sin confirmacion de dispositivo
                    IF NOT #ConfirmacionDisp[#s_Indice] AND #Pulso1Seg THEN
                        #s_TiempoActual += 1;
                    END_IF;
                    
                    //  Tiempo cumplido
                    IF (#s_TiempoActual >= #TiempoEntreEtapas) THEN
                        #s_TiempoActual := 0;
                        #s_Indice -= 1; //  Avanzamos al dispositivo anterior
                        
                        IF #s_Indice < 1 THEN
                            #s_EtapaActual := #REPOSO;
                        END_IF;
                    END_IF;
                    
                    //  Si se vuelve a dar marcha, invertimos dirección nuevamente
                    IF #MarchaSecuenciaParo THEN
                        #s_TiempoActual := 0;
                        #s_EtapaActual := #PARANDO;
                    END_IF;
                    
                    
                #FINALIZADO:
                    
                    IF NOT #MarchaSecuenciaParo THEN
                        #s_TiempoActual := 0;
                        //  Empezamos a rearmar desde el último dispositivo que se paro
                        #s_Indice := #s_NumeroDispositivos;
                        #s_EtapaActual := #REARMANDO;
                    END_IF;
                    
                ELSE
                    #s_EtapaActual := #REPOSO;
            END_CASE;
            
        END_REGION SECUENCIADOR_BIDIRECCIONAL
        
        
        // ==========================================================================================================
        REGION MAPEADO_DINAMICO_DE_SALIDAS
            
            #SecReposo := (#s_EtapaActual = #REPOSO);
            #SecParoFinalizada := (#s_EtapaActual = #FINALIZADO);
            
            //  Mapeo vectorial de comandos físicos
            FOR #for_i := 1 TO #N_MAX DO
                
                //  Si el índice iterativo es menor que nuestro puntero de etapa actual, 
                //  significa que ya hemos pasado por él en la secuencia de paro y debe estar detenido.
                IF #for_i < #s_Indice THEN
                    #ParoDispositivo[#for_i] := TRUE;
                    
                    //  Si el índice coincide con el puntero y estamos parando, forzamos la salida a TRUE
                ELSIF (#for_i = #s_Indice) AND (#s_EtapaActual = #PARANDO) THEN
                    #ParoDispositivo[#for_i] := TRUE;
                    
                    //  Para el resto de condiciones
                ELSE
                    #ParoDispositivo[#for_i] := FALSE;
                END_IF;
                
            END_FOR;
            
        END_REGION MAPEADO_DINAMICO_DE_SALIDAS
        
    END_NETWORK
END_FUNCTION_BLOCK
