{
    S7_Author := "ABH";
    S7_BlockComment := "MLC_44J";
    S7_BlockNumber := "15150";
    S7_BlockTitle := "MLC_42L";
    S7_EditorMode := "SCL";
    S7_Family := "ZeusControl";
    S7_IECCheck := "TRUE";
    S7_Optimized := "TRUE";
    S7_Version := "1.0"
}
FUNCTION_BLOCK "FB15150_ZC_SEC_S7COM"
    VAR_INPUT
        { S7_MLC := "MLC_4vr" }
        Habilitar : Bool := FALSE;
        { S7_MLC := "MLC_38z" }
        Iniciar : Bool;
        { S7_MLC := "MLC_4x4" }
        Ack : Bool;
        { S7_MLC := "MLC_4Zq" }
        Pulso1Seg : Bool;
        { S7_MLC := "MLC_4vy" }
        NumeroBloques : Int := 1;
        { S7_MLC := "MLC_3Tv" }
        TiempoEspera : Int := 10;
        NDR_DONE : Array[1..#N_MAX] of Bool;
        ERROR : Array[1..#N_MAX] of Bool;
    END_VAR
    VAR_OUTPUT
        { S7_MLC := "MLC_3m5" }
        Finalizado : Bool;
        { S7_MLC := "MLC_4P8" }
        EnEjecuccion : Bool;
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_4Ba"
        }
        AlgunError : Bool := FALSE;
        EnError : Array[1..#N_MAX] of Bool := [
            8(FALSE)
        ];
        REQ : Array[1..#N_MAX] of Bool;
    END_VAR
    VAR RETAIN
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_4dP";
            S7_Visibility := "Hidden := External"
        }
        s_Etapa : Int := #REPOSO;
    END_VAR
    VAR
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_Ph";
            S7_Visibility := "Hidden := External"
        }
        s_IndiceActual : Int;
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_V6";
            S7_Visibility := "Hidden := External"
        }
        s_AlgunError : Bool := FALSE;
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_LF";
            S7_Visibility := "Hidden := External"
        }
        s_NumeroBloques : Int;
    END_VAR
    VAR RETAIN
        {
            S7_Access := "ReadOnly := External";
            S7_MLC := "MLC_4Vt";
            S7_Visibility := "Hidden := External"
        }
        s_TiempoEsperaActual : Int;
    END_VAR
    VAR
        {
            S7_Access := "ReadOnly := External";
            S7_Visibility := "Hidden := External"
        }
        s_Error : Array[1..#N_MAX] of Bool := [
            8(FALSE)
        ];
    END_VAR
    VAR RETAIN
        {
            S7_Access := "ReadOnly := External";
            S7_Visibility := "Hidden := External"
        }
        s_NumeroError : Array[1..#N_MAX] of UInt;
        {
            S7_Access := "ReadOnly := External";
            S7_Visibility := "Hidden := External"
        }
        s_NumeroDone : Array[1..#N_MAX] of UInt;
    END_VAR
    VAR_TEMP
        for_i : Int;
    END_VAR
    VAR CONSTANT
        { S7_MLC := "MLC_4p6" }
        REPOSO : Int := 0;
        { S7_MLC := "MLC_4jG" }
        PROCESANDO : Int := 10;
        { S7_MLC := "MLC_376" }
        N_MAX : Int := 8;
    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 `FB15150_ZC_SEC_S7COM` implementa un gestor de comunicaciones secuenciales S7 (PUT/GET) que ejecuta, de forma ordenada y con gestión de errores, una secuencia de peticiones a otros tantos bloques de comunicación. Garantiza que las operaciones se lancen una a una, esperando a la finalización de cada una (correcta o con error) antes de continuar con la siguiente, y permite acotar el tiempo de espera por petición para detectar bloqueos de la comunicación.
        
        Funcionalidades principales:
        - Habilitación y Ack: cuando el bloque se deshabilita (`#Habilitar := FALSE`) o se activa la entrada de acuse `#Ack`, se reinicializan todas las variables internas y de salida, se vacían los flags de error, se resetea la etapa a `REPOSO` y se sale del bloque mediante `RETURN`.
        - Límite de dimensionamiento: se valida `#NumeroBloques` contra la constante de dimensionamiento `#N_MAX`, acotando el valor entre 1 y `#N_MAX`.
        - Arranque ordenado: cuando se recibe la entrada `#Iniciar` estando en `REPOSO`, se inicializa el índice de proceso, se limpian las peticiones y los flags de error, y se transita a la etapa `PROCESANDO`.
        - Procesamiento secuencial: para cada bloque actual, se lanza la activación de `#REQ` y se monitorizan las señales de fin `#NDR_DONE` y `#ERROR`. Se avanza al siguiente bloque tanto en caso de éxito como de error.
        - Control de tiempo de espera: si la petición actual no finaliza antes de cumplir `#TiempoEspera` (incrementado con `#Pulso1Seg`), se da por fallida y se avanza al siguiente bloque, contabilizándolo como error.
        - Salidas de estado: `#EnEjecuccion` indica que la secuencia está en curso (`#s_Etapa <> REPOSO`) y `#Finalizado` indica que la secuencia ha concluido. Por cada bloque se expone `#EnError` y un `#AlgunError` global que se activa si cualquier petición ha fallado.
        
        > **Restricciones:** Ninguna.
        
        ---
        ### Dependencias Requeridas
        | Tipo | Elementos |
        |------|-----------|
        | FC   | - |
        | FB   | - |
        | DB   | - |
        | UDT  | - |
            
        ---
        ### Historial de Cambios
        | Versión  | Fecha      | Técnico | Cambios |
        |----------|------------|---------|---------|
        | 00.00.01 | 24.05.2023 | (ABH)   | Primera versión. |
        | 00.00.02 | 21.09.2023 | (ABH)   | Cambio a SCL. |
        | 00.00.03 | 24.08.2025 | (ABH)   | Se añade tiempo de espera para dar como error. |
        | 01.00.00 | 02.07.2026 | (ABH)   | Refactorización de lógica para estandarización. Se añade constante de dimensionamiento. |
        *)
        END_REGION DESCRIPCION
        
        
        // ==========================================================================================================
        REGION HABILITACION
            
            //  En caso de no estar habilitado el bloque o pulsar el boton ACK, reinicializamos valores y salimos inmediatamente
            IF NOT #Habilitar OR #Ack THEN
                #s_AlgunError := FALSE;
                FOR #for_i := 1 TO #N_MAX DO
                    #REQ[#for_i] := FALSE;
                    #s_Error[#for_i] := FALSE;
                    #s_NumeroDone[#for_i] := 0;
                    #s_NumeroError[#for_i] := 0;
                    #EnError[#for_i] := FALSE;
                END_FOR;
                
                #s_Etapa := #REPOSO;
                #s_TiempoEsperaActual := 0;
                #s_IndiceActual := 1;
                
                #Finalizado := FALSE;
                #EnEjecuccion := FALSE;
                #AlgunError := FALSE;
                
                RETURN;
            END_IF;
            
        END_REGION HABILITACION
        
        
        // ==========================================================================================================
        REGION LIMITES
            
            // Traspaso de numero de bloques a variable interna
            #s_NumeroBloques := #NumeroBloques;
            
            // Limite de bloques
            IF #s_NumeroBloques > #N_MAX THEN
                #s_NumeroBloques := #N_MAX;
            ELSIF #s_NumeroBloques < 1 THEN
                #s_NumeroBloques := 1;
            END_IF;
            
        END_REGION LIMITES
        
        
        // ==========================================================================================================
        REGION ORDEN_DE_INICIO
            
            IF #Habilitar AND #s_Etapa = #REPOSO AND #Iniciar THEN
                
                #s_AlgunError := FALSE;
                #s_Etapa := #PROCESANDO;
                #s_IndiceActual := 1; // Apuntamos al primer BLOQUE
                
                // Reinicializamos las variables REQ y ERROR antes de empezar
                FOR #for_i := 1 TO #N_MAX DO
                    #REQ[#for_i] := FALSE;
                    #s_Error[#for_i] := FALSE;
                END_FOR;
                
                #s_TiempoEsperaActual := 0;
            END_IF;
            
        END_REGION ORDEN_DE_INICIO
        
        
        // ==========================================================================================================
        REGION SECUENCIA
            
            CASE #s_Etapa OF
                    
                #REPOSO:
                    ; //    Esperando orden de inicio
                    
                #PROCESANDO:
                    
                    //  Verificacion de final de la secuencia
                    IF #s_IndiceActual > #s_NumeroBloques THEN
                        //  Si hemos procesado todos los bloques, finalizamos la secuencia
                        #s_Etapa := #REPOSO;
                        RETURN;
                    END_IF;
                    
                    //  Procesamos
                    REGION GESTION_COMUNICACIONES
                        
                        //  Lanzamos orden request para el bloque de comunicaciones
                        #REQ[#s_IndiceActual] := TRUE;
                        
                        //  Esperamos a que finalice correctamente el bloque de comunicaciones
                        IF #NDR_DONE[#s_IndiceActual] THEN
                            
                            #REQ[#s_IndiceActual] := FALSE;
                            #s_NumeroDone[#s_IndiceActual] += 1;
                            #s_TiempoEsperaActual := 0;
                            
                            //  Avanzamos al siguiente bloque de comunicaciones
                            #s_IndiceActual += 1;
                            
                            //  En caso de error o Timeout
                        ELSIF #ERROR[#s_IndiceActual] OR #s_TiempoEsperaActual >= #TiempoEspera THEN
                            
                            #s_Error[#s_IndiceActual] := TRUE;
                            #REQ[#s_IndiceActual] := FALSE;
                            #s_NumeroError[#s_IndiceActual] += 1;
                            #s_TiempoEsperaActual := 0;
                            
                            //  Avanzamos al siguiente bloque de comunicaciones
                            #s_IndiceActual += 1;
                            
                        END_IF;
                        
                        //  Contador de tiempo de Timeout
                        IF #Pulso1Seg THEN
                            #s_TiempoEsperaActual += 1;
                        END_IF;
                        
                    END_REGION GESTION_COMUNICACIONES
                    
                ELSE
                    #s_Etapa := #REPOSO;
                    
            END_CASE;
            
        END_REGION SECUENCIA
        
        
        // ==========================================================================================================
        REGION SALIDAS
            
            #Finalizado := (#s_Etapa = #REPOSO);
            #EnEjecuccion := (#s_Etapa <> #REPOSO);
            
            //  Gestion señales de salida de bloque
            #s_AlgunError := FALSE;
            FOR #for_i := 1 TO #N_MAX DO
                #EnError[#for_i] := #s_Error[#for_i];
                IF #s_Error[#for_i] THEN
                    #s_AlgunError := TRUE;
                END_IF;
            END_FOR;
            
            #AlgunError := #s_AlgunError;
            
        END_REGION SALIDAS
        
        
    END_NETWORK
END_FUNCTION_BLOCK
