{
    S7_Author := "HCR";
    S7_BlockComment := "MLC_3dQ";
    S7_BlockTitle := "MLC_42k";
    S7_EditorMode := "SCL";
    S7_Family := "ZeusControl";
    S7_IECCheck := "TRUE";
    S7_Optimized := "TRUE";
    S7_Version := "2.0"
}
FUNCTION "FC1_ZC_SISTEMA" : Void
    VAR_INPUT
        { S7_MLC := "MLC_576" }
        CPU_MarcasCiclo : Byte;
        { S7_MLC := "MLC_4Mc" }
        CPU_MarcasSistema : Byte;
        { S7_MLC := "MLC_3B7" }
        RedProfinet : HW_IOSYSTEM;
    END_VAR
    VAR_TEMP
        { S7_MLC := "MLC_3bT" }
        for_i : Int;
        { S7_MLC := "MLC_4GP" }
        t_RetVal : Int;
    END_VAR

    { S7_Language := "SCL" }
    NETWORK
        REGION DESCRIPCION
        (*
        ###### (C)Copyright ZeusControl 2020 - 2026
        
        ---
        ### Información del Sistema
        | Hardware Compatible | Entorno de Ingeniería |
        |---------------------|-----------------------|
        | PLC serie 1200/1500 | TIA Portal 18         |
        
        ---
        > **Restricciones:** Ninguna.
        
        ---
        ### Descripción Funcional
        
        Función central para la gestión de marcas de sistema, temporizaciones generales, usuarios y resets del sistema. 
        
        En ella se realizan las siguientes acciones:
        
        - Cálculo dinámico del tiempo de ciclo del OB1 (en milisegundos) evaluando la variable auxiliar con la instrucción RUNTIME. 
        - Tratamiento y registro de trazabilidad de los 10 pulsadores de acuse de alarmas y del pulsador de modificación de fecha/hora mediante la función FC15026_ZC_PULSADOR. 
        - Consolidación del acuse general (ACK), agrupando las activaciones del PLC de todos los pulsadores de reconocimiento en una única marca. 
        - Generación de marcas estáticas (ON fijo y OFF fijo) y captura del primer ciclo de escáner tras el arranque. 
        - Creación de pulsos temporizados (100 ms, 1 s, 2 s, 5 s, 1 min, 1 h) y señales Flip-Flop a partir de las marcas de ciclo del hardware de la CPU. 
        - Lectura constante de la fecha y hora local (RD_LOC_T) y ejecución de la escritura en el sistema (WR_SYS_T) ante una orden del HMI. 
        - Escaneo continuo de las órdenes de selección para determinar y fijar el usuario actualmente activo en el sistema. 
        - Ejecución bajo demanda del reset general del array de entidades (solo aplicable con procesos parados), reinicializando sus variables a estados de disponibilidad, limpieza y vacío. 
        - Llamada a la rutina DB3_CHECKSUM para la validación y gestión de la suma de comprobación del programa. 
        
        ---
        ### Dependencias Requeridas
        | Tipo | Elementos |
        |------|-----------|
        | FC   | `FC15026_ZC_PULSADOR` |
        | FB   | `FB3_ZC_CHECKSUM` |
        | DB   | `DB1_SYS`, `DB3_CHECKSUM`, `DB6_ENTIDAD` |
        | UDT  | - |
        
        ---
        ### Historial de Cambios
        | Versión  | Fecha      | Técnico | Cambios |
        |----------|------------|---------|---------|
        | 01.00.00 | 03.11.2022 | (HCR)   | Primera version. |
        | 01.00.01 | 02.06.2023 | (ABH)   | Optimizacion de bloque para integrar en libreria. Se eliminan marcas no utilizadas. Se reorganizan las variables y se ponen bien los comentarios. |
        | 01.00.02 | 24.04.2025 | (HCR)   | Se añade gestion de idioma. |
        | 01.00.03 | 11.09.2025 | (ABH)   | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
        | 02.00.00 | 25.03.2026 | (ABH)   | Se cambia lenguaje a SCL. Se eliminan idiomas. Se añade watchdog para comunicaciones. Se integra estado red profinet. |
        *)
        END_REGION DESCRIPCION
        
        
        //  ==========================================================================================================
        REGION TIEMPO_DE_CICLO
            
            //  Se almacena el tiempo de ciclo del OB1
            "DB1_SYS".TiempoCiclo := LREAL_TO_REAL(RUNTIME("DB1_SYS".Aux.auxRuntime));
            "DB1_SYS".TiempoCiclo := ("DB1_SYS".TiempoCiclo * 1000.0);
            
        END_REGION TIEMPO_DE_CICLO
        
        
        //  ==========================================================================================================
        REGION GESTION_MARCAS_GENERALES_Y_FLANCOS
            
            //  ==========================================================================================================
            //  Marca siempre a ON
            "DB1_SYS".OFF := FALSE;
            
            //  Marca siempre a OFF
            "DB1_SYS".ON := TRUE;
            
            //  Marca primer arranque
            "DB1_SYS".ARRANQUE := #CPU_MarcasSistema.%X0;
            
            //  ==========================================================================================================
            //  Pulso por 100 ms
            "DB1_SYS".Pulso100ms := #CPU_MarcasCiclo.%X0 AND NOT "DB1_SYS".Aux.P_Trig_Pulso100ms;
            
            //  Pulso por 1 segundo
            "DB1_SYS".Pulso1seg := #CPU_MarcasCiclo.%X5 AND NOT "DB1_SYS".Aux.P_Trig_Pulso1Seg;
            
            //  Pulso por 2 segundos
            "DB1_SYS".Pulso2seg := FALSE;
            IF "DB1_SYS".Pulso1seg THEN
                "DB1_SYS".Aux.Contador2seg += 1;
                IF "DB1_SYS".Aux.Contador2seg >= 2 THEN
                    "DB1_SYS".Pulso2seg := TRUE;
                    "DB1_SYS".Aux.Contador2seg := 0;
                END_IF;
            END_IF;
            
            //  Pulso por 5 segundos
            "DB1_SYS".Pulso5seg := FALSE;
            IF "DB1_SYS".Pulso1seg THEN
                "DB1_SYS".Aux.Contador5seg += 1;
                IF "DB1_SYS".Aux.Contador5seg >= 5 THEN
                    "DB1_SYS".Pulso5seg := TRUE;
                    "DB1_SYS".Aux.Contador5seg := 0;
                END_IF;
            END_IF;
            
            //  Pulso por 1 minuto
            "DB1_SYS".Pulso1min := FALSE;
            IF "DB1_SYS".Pulso1seg THEN
                "DB1_SYS".Aux.Contador1min += 1;
                IF "DB1_SYS".Aux.Contador1min >= 60 THEN
                    "DB1_SYS".Pulso1min := TRUE;
                    "DB1_SYS".Aux.Contador1min := 0;
                END_IF;
            END_IF;
            
            //  Pulso por 1 hora
            "DB1_SYS".Pulso1h := FALSE;
            IF "DB1_SYS".Pulso1seg THEN
                "DB1_SYS".Aux.Contador1h += 1;
                IF "DB1_SYS".Aux.Contador1h >= 3600 THEN
                    "DB1_SYS".Pulso1h := TRUE;
                    "DB1_SYS".Aux.Contador1h := 0;
                END_IF;
            END_IF;
            
            //  ==========================================================================================================
            //  Flip Flop 1 segundo
            IF "DB1_SYS".Pulso1seg AND NOT "DB1_SYS".FlipFlop1seg THEN
                "DB1_SYS".FlipFlop1seg := TRUE;
            ELSIF "DB1_SYS".Pulso1seg AND "DB1_SYS".FlipFlop1seg THEN
                "DB1_SYS".FlipFlop1seg := FALSE;
            END_IF;
            
            //  Flip Flop 2 segundos
            IF "DB1_SYS".Pulso2seg AND NOT "DB1_SYS".FlipFlop2seg THEN
                "DB1_SYS".FlipFlop2seg := TRUE;
            ELSIF "DB1_SYS".Pulso2seg AND "DB1_SYS".FlipFlop2seg THEN
                "DB1_SYS".FlipFlop2seg := FALSE;
            END_IF;
            
            //  Flip Flop 5 segundos
            IF "DB1_SYS".Pulso5seg AND NOT "DB1_SYS".FlipFlop5seg THEN
                "DB1_SYS".FlipFlop5seg := TRUE;
            ELSIF "DB1_SYS".Pulso5seg AND "DB1_SYS".FlipFlop5seg THEN
                "DB1_SYS".FlipFlop5seg := FALSE;
            END_IF;
            
            //  ==========================================================================================================
            //  Watchdog
            IF "DB1_SYS".Pulso100ms THEN
                "DB1_SYS".WatchDog += 1;
                IF "DB1_SYS".WatchDog > 20000 THEN
                    "DB1_SYS".WatchDog := 0;
                END_IF;
            END_IF;
            
            //  ==========================================================================================================
            //  Estados anteriores
            "DB1_SYS".Aux.P_Trig_Pulso100ms := #CPU_MarcasCiclo.%X0;
            "DB1_SYS".Aux.P_Trig_Pulso1Seg := #CPU_MarcasCiclo.%X5;
            
        END_REGION GESTION_MARCAS_GENERALES_Y_FLANCOS
        
        
        //  ==========================================================================================================
        REGION ARRANQUE
            
            IF "DB1_SYS".ARRANQUE THEN
                
                //  Actualizamos la fecha y hora nueva para editar desde HMI/SCADA
                #t_RetVal := RD_LOC_T("DB1_SYS".FechaHoraNuevaManual);
                
                //  Reseteamos marcas de simulacion y FAT
                "DB1_SYS".SIM := FALSE;
                "DB1_SYS".FAT := FALSE; 
                
            END_IF;
            
        END_REGION ARRANQUE
        
        
        //  ==========================================================================================================
        REGION GESTION_PULSADORES
            
            //  Gestion pulsadores de ack
            "DB1_SYS".Pulsador.Ack[0].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_01",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[0]);
            
            "DB1_SYS".Pulsador.Ack[1].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_02",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[1]);
            
            "DB1_SYS".Pulsador.Ack[2].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_03",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[2]);
            
            "DB1_SYS".Pulsador.Ack[3].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_04",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[3]);
            
            "DB1_SYS".Pulsador.Ack[4].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_05",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[4]);
            
            "DB1_SYS".Pulsador.Ack[5].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_06",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[5]);
            
            "DB1_SYS".Pulsador.Ack[6].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_07",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[6]);
            
            "DB1_SYS".Pulsador.Ack[7].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_08",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[7]);
            
            "DB1_SYS".Pulsador.Ack[8].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_09",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[8]);
            
            "DB1_SYS".Pulsador.Ack[9].Permiso := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_ACK_10",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.Ack[9]);
            
            //  Gestion pulsador cambio de fecha y hora
            "DB1_SYS".Pulsador.EscribirFechaHora.Permiso := TRUE;
            "DB1_SYS".Pulsador.EscribirFechaHora.Visibilidad := TRUE;
            "FC15026_ZC_PULSADOR"(Pulso1Seg := "DB1_SYS".Pulso1seg,
                                  SP_Tiempo := 0,
                                  TrazaCategoria := "TRZ_CAT_1_SYS",
                                  TrazaIdProceso := 0,
                                  TrazaIdPulsador := "TRZ_SYS_PUL_FECHA",
                                  Usuario := "DB1_SYS".Usuario.Actual,
                                  FechaHoraActual := "DB1_SYS".FechaHoraActual,
                                  Pulsador := "DB1_SYS".Pulsador.EscribirFechaHora);
            
        END_REGION GESTION_PULSADORES
        
        
        //  ==========================================================================================================
        REGION GESTION_ACUSE_GENERAL
            
            //  Activacion de marca de acuse general
            "DB1_SYS".ACK :=
            "DB1_SYS".Pulsador.Ack[0].Plc OR
            "DB1_SYS".Pulsador.Ack[1].Plc OR
            "DB1_SYS".Pulsador.Ack[2].Plc OR
            "DB1_SYS".Pulsador.Ack[3].Plc OR
            "DB1_SYS".Pulsador.Ack[4].Plc OR
            "DB1_SYS".Pulsador.Ack[5].Plc OR
            "DB1_SYS".Pulsador.Ack[6].Plc OR
            "DB1_SYS".Pulsador.Ack[7].Plc OR
            "DB1_SYS".Pulsador.Ack[8].Plc OR
            "DB1_SYS".Pulsador.Ack[9].Plc;
            
        END_REGION GESTION_ACUSE_GENERAL
        
        
        //  ==========================================================================================================
        REGION FECHA_Y_HORA
            
            //  Lectura de fecha y hora actual
            #t_RetVal := RD_LOC_T("DB1_SYS".FechaHoraActual);
            
            //  Escritura de fecha y hora nueva escrita manualmente desde HMI
            IF "DB1_SYS".Pulsador.EscribirFechaHora.Plc THEN
                #t_RetVal := WR_SYS_T("DB1_SYS".FechaHoraNuevaManual);
            END_IF;
            
            //  Escritura de fecha y hora nueva escrita autoamticamente desde Script, Scada, etc...
            IF "DB1_SYS".SincronizarFecha THEN
                #t_RetVal := WR_SYS_T("DB1_SYS".FechaHoraNuevaAuto);
            END_IF;
            
        END_REGION FECHA_Y_HORA
        
        
        //  ==========================================================================================================
        REGION USUARIOS
            
            //  Gestion de usuario actual para trazas
            FOR #for_i := 0 TO 9 DO
                IF "DB1_SYS".Usuario.Seleccion[#for_i].Orden THEN
                    "DB1_SYS".Usuario.Actual := "DB1_SYS".Usuario.Seleccion[#for_i].Usuario;
                    "DB1_SYS".Usuario.Seleccion[#for_i].Orden := FALSE;
                    EXIT;
                END_IF;
            END_FOR;
            
        END_REGION USUARIOS
        
        
        //  ==========================================================================================================
        REGION RESET_ENTIDADES
            
            //  Reset todas las entidades
            //  CUIDADO!!!!!!!!
            //          TIENEN QUE ESTAR TODOS LOS PROCESOS PARADOS!!!!!!
            IF "DB1_SYS".ResetEntidades THEN
                FOR #for_i := 1 TO "N_MAX_ENTIDAD" DO
                    
                    "DB6_ENTIDAD".ENT[#for_i].Estado.EnProduccion :=
                    "DB6_ENTIDAD".ENT[#for_i].Estado.EnCIP :=
                    "DB6_ENTIDAD".ENT[#for_i].Estado.EnSIP :=
                    "DB6_ENTIDAD".ENT[#for_i].Estado.ConProducto :=
                    "DB6_ENTIDAD".ENT[#for_i].Estado.ConAgua :=
                    "DB6_ENTIDAD".ENT[#for_i].Estado.Vacia :=
                    "DB6_ENTIDAD".ENT[#for_i].Estado.Sucia := FALSE;
                    
                    "DB6_ENTIDAD".ENT[#for_i].Habilitar :=
                    "DB6_ENTIDAD".ENT[#for_i].Estado.Disponible :=
                    "DB6_ENTIDAD".ENT[#for_i].Estado.Limpia := TRUE;
                    
                    "DB6_ENTIDAD".ENT[#for_i].Estado.CodEstado := 0;
                    "DB6_ENTIDAD".ENT[#for_i].Asignacion := 0;
                    
                    "DB6_ENTIDAD".ENT[#for_i].ID := #for_i;
                    
                END_FOR;
                
                "DB1_SYS".ResetEntidades := FALSE;
            END_IF;
            
        END_REGION RESET_ENTIDADES
        
        
        //  ==========================================================================================================
        REGION CHECKSUM
            
            //  Gestion de checksum del programa
            "DB3_CHECKSUM"();
            
        END_REGION CHECKSUM
        
        
        //  ==========================================================================================================
        REGION RED_PROFINET
            
            //  Comprobacion de red profinet
            #t_RetVal := DeviceStates(LADDR := #RedProfinet, MODE := 4, STATE := "DB1_SYS".RedProfinet);
            
        END_REGION RED_PROFINET
        
        
        
    END_NETWORK
END_FUNCTION
