FUNCTION_BLOCK "FB15150_ZC_SEC_S7COM"
TITLE = FB15150_ZC_SEC_GET
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//ZEUS CONTROL, S.A.
//(c)Copyright (2023) All Rights Reserved
//--------------------------------------------------------------------------------------
//
//Software:       TIA Portal 16
//Restricciones:  PLC serie 1200/1500
//
//Nombre:         FB15150_ZC_SEC_GET
//Descripcion:    Bloque de funcion para gestion de comunicaciones secuenciales GET.
//
//Dependencias:   
//    FC:         -
//    FB:         -
//    DB:         -
//    UDT:        -
//                                
//
//Change log:
//
//    Version     Fecha       Tecnico a cargo     Descripcion
//    
//    01.00.00    24.05.2023  (ABH)               Primera version
//    01.00.01    21.09.2023  (ABH)               Cambio a SCL.
//    
////=====================================================================================
   VAR_INPUT 
      Habilitar : Bool := FALSE;   // Habilitacion General
      Iniciar : Bool;   // Iniciar secuencia
      Ack : Bool;   // Acuse general para resetear la secuencia
      Pulso1Seg : Bool;   // Pulso 1 segundo
      NumeroBloques { S7_HiddenAssignment := 'HideIfNoParamAssigned'} : Int := 1;   // Numero de bloques de comunicacion en la secuencia
      TiempoEspera { S7_HiddenAssignment := 'HideIfNoParamAssigned'} : Int := 10;   // Tiempo espera estado bloque comunicacion
      NDR_DONE : Array[1..#N_MAX] of Bool;
      ERROR : Array[1..#N_MAX] of Bool;
   END_VAR

   VAR_OUTPUT 
      Finalizado : Bool;   // Secuencia finalizada
      EnEjecuccion : Bool;   // Secuencia en ejecucion
      AlgunError { ExternalWritable := 'False'} : Bool := FALSE;   // Algun error
      EnError { S7_HiddenAssignment := 'HideIfNoParamAssigned'} : Array[1..#N_MAX] of Bool := [8(FALSE)];
      REQ { S7_HiddenAssignment := 'HideIfNoParamAssigned'} : Array[1..#N_MAX] of Bool;
   END_VAR

   VAR RETAIN
      s_Etapa { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int := #REPOSO;   // Etapa actual
   END_VAR
   VAR 
      s_IndiceActual { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;   // Indice actual del bloque de comunicacion en ejecuccion
      s_AlgunError { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool := FALSE;   // Indicador de algun error
      s_NumeroBloques { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;   // Numero de bloques
   END_VAR
   VAR RETAIN
      s_TiempoEsperaActual { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;   // Tiempo de espera actual
   END_VAR
   VAR 
      s_Error { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[1..#N_MAX] of Bool := [8(FALSE)];
   END_VAR
   VAR RETAIN
      s_NumeroError { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[1..#N_MAX] of UInt;
      s_NumeroDone { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[1..#N_MAX] of UInt;
   END_VAR

   VAR_TEMP 
      for_i : Int;
   END_VAR

   VAR CONSTANT 
      REPOSO : Int := 0;   // Etapa reposo
      PROCESANDO : Int := 10;   // Etapa procesando
      N_MAX : Int := 8;   // Numero maximo de bloques de comunicacion
   END_VAR


BEGIN
	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_FUNCTION_BLOCK

