{
    S7_Author := "ABH";
    S7_BlockComment := "MLC_ax";
    S7_BlockNumber := "2025";
    S7_BlockTitle := "MLC_3tF";
    S7_EditorMode := "SCL";
    S7_Family := "ZeusControl";
    S7_Optimized := "TRUE";
    S7_Version := "1.0"
}
FUNCTION "FC2025_ZC_TOT" : Void
    VAR_INPUT
        { S7_MLC := "MLC_4Yz" }
        Pulso100ms : Bool;
        { S7_MLC := "MLC_39T" }
        Simulacion : Bool;
        { S7_MLC := "MLC_nD" }
        Arranque : Bool;
    END_VAR
    VAR_TEMP
        for_i : Int;
        t_N_MAX : Int;
        t_CaudalSegundos : LReal;
        t_LReal : LReal;
        t_RT_Dummy : LReal;
    END_VAR
    VAR CONSTANT
        { S7_MLC := "MLC_4ux" }
        VALOR_MAXIMO_CONTADORES : Real := 1000000.0;
        { S7_MLC := "MLC_4VC" }
        NUMERO_MAXIMO_DESBORDE : Int := 32000;
    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
        
        Funcion para la gestion de dispositivo de tipo Totalizador.
        
        En ella se realizan las siguientes acciones:
        
        - Se gestiona el multiplexado de los dispositivos para su uso en sistemas HMI/SCADA.
        - Se gestiona el estado del dispositivo
        - Se gestiona el contaje del totalizador por entrada digital o por entrada analogica
        
        ---
        ### Dependencias Requeridas
        | Tipo | Elementos |
        |------|-----------|
        | FC   | - |
        | FB   | - |
        | DB   | - |
        | UDT  | - |
            
        ---
        ### Historial de Cambios
        | Versión  | Fecha      | Técnico | Cambios |
        |----------|------------|---------|---------|
        | 00.00.01 | 15.10.2020 | (ABH)   | Primera version. |
        | 00.00.02 | 28.01.2022 | (HCR)   | Se añade corrección. |
        | 00.00.03 | 05.09.2023 | (HCR)   | Se añade contador por desborde  |
        | 00.00.04 | 18.09.2023 | (ABH)   | Se estandariza nombre variables para gestion de tiempo de ciclo para integracion de EA. |
        | 00.00.05 | 28.02.2024 | (ABH)   | Se añade gestion multiplexado para Scada (no indexado) |
        | 00.00.06 | 08.01.2025 | (ABH)   | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado. Se añade movimiento de TOT[0] a multiplexado en caso de que el indice sea 0.     |
        | 00.00.07 | 24.01.2025 | (ABH)   | Se modifica la logica del multiplexado, la asignacion de oldindex se cambia de sitio ya que de vez en cuando (dependiendo de cuando el HMI realiza la comunicacion asincrona con el PLC) no detectaba el cambio y se machacaban valores. |
        | 01.00.00 | 28.07.2025 | (ABH)   | Se extrae el multiplexado a FC independiente. Se corrige reset de ordenes reset totalizadores en multiplexado. Se añade totalizador usuario. |
        *)
        END_REGION DESCRIPCION
        
        
        REGION TIEMPO_CICLO_INTEGRACION
            
            //  Tiempo ciclo para integración EA
            "DB2025_TOT".TiempoCiclo.Actual := LREAL_TO_REAL(RUNTIME("DB2025_TOT".TiempoCiclo.auxMem));
            "DB2025_TOT".TiempoCiclo.Actual := ("DB2025_TOT".TiempoCiclo.Actual * 1000.0);
            
        END_REGION TIEMPO_CICLO_INTEGRACION
        
        
        REGION LOGICA_DEL_DISPOSITIVO
            
            //  Numero maximo de dispositivos para limitar en HMI/Scada
            "DB2025_TOT".NumDispositivos := "N_MAX_DISP_TOT";
            
            REGION COMPROBACION_N_MAX
                
                //  Si no hay dispositivos declarados, salimos del bloque inmediatamente
                IF "N_MAX_DISP_TOT" <= 0 THEN
                    RETURN;
                END_IF;
                
                //  Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
                #t_N_MAX := "N_MAX_DISP_TOT";
                
            END_REGION COMPROBACION_N_MAX
            
            FOR #for_i := 1 TO #t_N_MAX DO
                
                //  Dispositivo habilitado
                IF "DB2025_TOT".TOT[#for_i].Config_Habilitar THEN
                    
                    REGION LECTURA_VALORES
                        
                        REGION ENTRADA_PULSOS
                            
                            IF NOT #Simulacion THEN
                                
                                IF "DB2025_TOT".TOT[#for_i].Config_DireccionEntradaByte > 0 THEN
                                    
                                    //  Lectura de la entrada digital
                                    "DB2025_TOT".TOT[#for_i].Estado_Pulso := PEEK_BOOL(area := 16#81,
                                                                                       dbNumber := 0,
                                                                                       byteOffset := "DB2025_TOT".TOT[#for_i].Config_DireccionEntradaByte,
                                                                                       bitOffset := "DB2025_TOT".TOT[#for_i].Config_DireccionEntradaBit);
                                END_IF;
                            ELSE
                                "DB2025_TOT".TOT[#for_i].Estado_Pulso := #Pulso100ms;
                            END_IF;
                            
                        END_REGION ENTRADA_PULSOS
                        
                        
                        REGION CAUDAL
                            
                            IF "DB2025_TOT".TOT[#for_i].Estado_Valor > 0.0 THEN
                                #t_CaudalSegundos := (("DB2025_TOT".TOT[#for_i].Estado_Valor / 3600000.0 * "DB2025_TOT".TiempoCiclo.Actual));
                            ELSE
                                #t_CaudalSegundos := 0.0;
                            END_IF;
                            
                        END_REGION CAUDAL
                        
        
                        
                    END_REGION LECTURA_VALORES
                    
                    
                    REGION MODO_TRABAJO
                        
                        REGION TOTALIZADORES
                            
                            //  Orden activacion totalizadores
                            IF "DB2025_TOT".TOT[#for_i].Orden_Activar THEN
                                
                                //  0 = TOTALIZADOR POR PULSOS
                                //  1 = TOTALIZADOR POR EA
                                IF NOT "DB2025_TOT".TOT[#for_i].Config_Tipo THEN    //  Caudalímetro por Pulso
                                    
                                    IF "DB2025_TOT".TOT[#for_i].Estado_Pulso AND NOT "DB2025_TOT".TOT[#for_i].Aux_oldActivado THEN
                                        
                                        IF NOT "DB2025_TOT".TOT[#for_i].Config_IncrDecr THEN
                                            "DB2025_TOT".TOT[#for_i].Estado_Parcial += "DB2025_TOT".TOT[#for_i].Config_LitroXPulso;
                                            "DB2025_TOT".TOT[#for_i].Estado_Total += "DB2025_TOT".TOT[#for_i].Config_LitroXPulso;
                                            "DB2025_TOT".TOT[#for_i].Estado_Proceso += "DB2025_TOT".TOT[#for_i].Config_LitroXPulso;
                                        ELSE
                                            "DB2025_TOT".TOT[#for_i].Estado_Parcial -= "DB2025_TOT".TOT[#for_i].Config_LitroXPulso;
                                            "DB2025_TOT".TOT[#for_i].Estado_Total -= "DB2025_TOT".TOT[#for_i].Config_LitroXPulso;
                                            "DB2025_TOT".TOT[#for_i].Estado_Proceso -= "DB2025_TOT".TOT[#for_i].Config_LitroXPulso;
                                        END_IF;
                                        
                                    END_IF;
        
                                    
                                ELSE
                                    
                                    //  Totalizador por EA
                                    IF NOT "DB2025_TOT".TOT[#for_i].Config_IncrDecr THEN
                                        "DB2025_TOT".TOT[#for_i].Estado_Parcial += #t_CaudalSegundos;
                                        "DB2025_TOT".TOT[#for_i].Estado_Total += #t_CaudalSegundos;
                                        "DB2025_TOT".TOT[#for_i].Estado_Proceso += #t_CaudalSegundos;
                                    ELSE
                                        "DB2025_TOT".TOT[#for_i].Estado_Parcial -= #t_CaudalSegundos;
                                        "DB2025_TOT".TOT[#for_i].Estado_Total -= #t_CaudalSegundos;
                                        "DB2025_TOT".TOT[#for_i].Estado_Proceso -= #t_CaudalSegundos;
                                    END_IF;
                                    
                                END_IF;
                                
                            END_IF;
                            
                        END_REGION TOTALIZADORES
                        
        
                        REGION TOTALIZADOR_USUARIO
                            
                            IF "DB2025_TOT".TOT[#for_i].Orden_Usuario THEN
                                
                                //  0 = TOTALIZADOR POR PULSOS
                                //  1 = TOTALIZADOR POR EA
                                IF NOT "DB2025_TOT".TOT[#for_i].Config_Tipo THEN    //  Caudalímetro por Pulso
                                    
                                    IF "DB2025_TOT".TOT[#for_i].Estado_Pulso AND NOT "DB2025_TOT".TOT[#for_i].Aux_oldActivado THEN
                                        
                                        IF NOT "DB2025_TOT".TOT[#for_i].Config_IncrDecr THEN
                                            "DB2025_TOT".TOT[#for_i].Estado_Usuario += "DB2025_TOT".TOT[#for_i].Config_LitroXPulso;
                                        ELSE
                                            "DB2025_TOT".TOT[#for_i].Estado_Usuario -= "DB2025_TOT".TOT[#for_i].Config_LitroXPulso;
                                        END_IF;
                                        
                                    END_IF;
                                    
                                ELSE
                                    
                                    //  Totalizador por EA
                                    IF NOT "DB2025_TOT".TOT[#for_i].Config_IncrDecr THEN
                                        "DB2025_TOT".TOT[#for_i].Estado_Usuario += #t_CaudalSegundos;
                                    ELSE
                                        "DB2025_TOT".TOT[#for_i].Estado_Usuario -= #t_CaudalSegundos;
                                    END_IF;
                                    
                                END_IF;
                                
                            END_IF;
                            
                        END_REGION TOTALIZADOR_USUARIO
                        
                        
                        REGION CALCULO_CAUDAL_TEORICO                    
                            
                            IF NOT "DB2025_TOT".TOT[#for_i].Config_Tipo AND "DB2025_TOT".TOT[#for_i].Orden_Activar THEN
                                
                                //  Calculo teorico L/H
                                IF #Pulso100ms THEN
                                    "DB2025_TOT".TOT[#for_i].Aux_AcuTiempo += 1;
                                    
                                    IF "DB2025_TOT".TOT[#for_i].Aux_AcuTiempo > 10 THEN
                                        #t_LReal := "DB2025_TOT".TOT[#for_i].Estado_Total - "DB2025_TOT".TOT[#for_i].Aux_TotalizadorMem;
                                        
                                        "DB2025_TOT".TOT[#for_i].Estado_Valor := LREAL_TO_REAL(#t_LReal) * 36000.0 / "DB2025_TOT".TOT[#for_i].Aux_AcuTiempo;
                                        
                                        "DB2025_TOT".TOT[#for_i].Aux_AcuTiempo := 0;
                                        "DB2025_TOT".TOT[#for_i].Aux_TotalizadorMem := "DB2025_TOT".TOT[#for_i].Estado_Total;
                                    END_IF;
                                END_IF;
                                
                            ELSIF NOT "DB2025_TOT".TOT[#for_i].Config_Tipo AND "DB2025_TOT".TOT[#for_i].Orden_Usuario THEN
                                
                                //  Calculo teorico L/H
                                IF #Pulso100ms THEN
                                    "DB2025_TOT".TOT[#for_i].Aux_AcuTiempo += 1;
                                    
                                    IF "DB2025_TOT".TOT[#for_i].Aux_AcuTiempo > 10 THEN
                                        #t_LReal := "DB2025_TOT".TOT[#for_i].Estado_Usuario - "DB2025_TOT".TOT[#for_i].Aux_TotalizadorMem;
                                        
                                        "DB2025_TOT".TOT[#for_i].Estado_Valor := LREAL_TO_REAL(#t_LReal) * 36000.0 / "DB2025_TOT".TOT[#for_i].Aux_AcuTiempo;
                                        
                                        "DB2025_TOT".TOT[#for_i].Aux_AcuTiempo := 0;
                                        "DB2025_TOT".TOT[#for_i].Aux_TotalizadorMem := "DB2025_TOT".TOT[#for_i].Estado_Usuario;
                                    END_IF;
                                END_IF;
                                
                            ELSE
                                
                                "DB2025_TOT".TOT[#for_i].Aux_TotalizadorMem := 0.0;
                                
                            END_IF;
                            
                        END_REGION
                        
                        //  Estado anterior del pulso
                        "DB2025_TOT".TOT[#for_i].Aux_oldActivado := "DB2025_TOT".TOT[#for_i].Estado_Pulso;
                        
                    END_REGION MODO_TRABAJO
                    
                    
                    REGION DIAGNOSIS
                        
                        //  Reset totalizador total por desbordamiento
                        IF "DB2025_TOT".TOT[#for_i].Estado_Total >= #VALOR_MAXIMO_CONTADORES THEN
                            "DB2025_TOT".TOT[#for_i].Estado_DesbordeTotal += 1;
                            "DB2025_TOT".TOT[#for_i].Estado_Total := 0.0;
                        END_IF;
                        
                        //  Reset totalizador parcial por desbordamiento
                        IF "DB2025_TOT".TOT[#for_i].Estado_Parcial >= #VALOR_MAXIMO_CONTADORES THEN
                            "DB2025_TOT".TOT[#for_i].Estado_DesbordeParcial += 1;
                            "DB2025_TOT".TOT[#for_i].Estado_Parcial := 0.0;
                        END_IF;
                        
                        //  Reset totalizador usuario por desbordamiento
                        IF "DB2025_TOT".TOT[#for_i].Estado_Usuario >= #VALOR_MAXIMO_CONTADORES THEN
                            "DB2025_TOT".TOT[#for_i].Estado_DesbordeUsuario += 1;
                            "DB2025_TOT".TOT[#for_i].Estado_Usuario := 0.0;
                        END_IF;
                        
                        //  Reset totalizador parcial
                        IF "DB2025_TOT".TOT[#for_i].Orden_ResetParcial OR "DB2025_TOT".TOT[#for_i].Estado_DesbordeParcial > #NUMERO_MAXIMO_DESBORDE THEN
                            "DB2025_TOT".TOT[#for_i].Estado_Parcial := 0.0;
                            "DB2025_TOT".TOT[#for_i].Estado_DesbordeParcial := 0;
                            "DB2025_TOT".TOT[#for_i].Orden_ResetParcial := FALSE;
                        END_IF;
                        
                        //  Reset totalizador total
                        IF "DB2025_TOT".TOT[#for_i].Orden_ResetTotal OR "DB2025_TOT".TOT[#for_i].Estado_DesbordeTotal > #NUMERO_MAXIMO_DESBORDE THEN
                            "DB2025_TOT".TOT[#for_i].Estado_Total := 0.0;
                            "DB2025_TOT".TOT[#for_i].Estado_DesbordeTotal := 0;
                            "DB2025_TOT".TOT[#for_i].Orden_ResetTotal := FALSE;
                        END_IF;
                        
                        //  Reset totalizador proceso
                        IF "DB2025_TOT".TOT[#for_i].Orden_ResetProceso THEN
                            "DB2025_TOT".TOT[#for_i].Estado_Proceso := 0.0;
                            "DB2025_TOT".TOT[#for_i].Orden_ResetProceso := FALSE;
                        END_IF;
                        
                        //  Reset totalizador usuario
                        IF "DB2025_TOT".TOT[#for_i].Orden_ResetUsuario THEN
                            "DB2025_TOT".TOT[#for_i].Estado_Usuario := 0.0;
                            "DB2025_TOT".TOT[#for_i].Orden_ResetUsuario := FALSE;
                        END_IF;
                        
                    END_REGION
                    
                ELSE
                    
                    //  Dispositivo no habilitado
                    "DB2025_TOT".TOT[#for_i].Orden_Activar := FALSE;
                    "DB2025_TOT".TOT[#for_i].Orden_ResetParcial := FALSE;
                    "DB2025_TOT".TOT[#for_i].Orden_ResetProceso := FALSE;
                    "DB2025_TOT".TOT[#for_i].Orden_ResetTotal := FALSE;
                    "DB2025_TOT".TOT[#for_i].Orden_ResetUsuario := FALSE;
                    "DB2025_TOT".TOT[#for_i].Estado_Valor := 0.0;
                    "DB2025_TOT".TOT[#for_i].Estado_Parcial := 0.0;
                    "DB2025_TOT".TOT[#for_i].Estado_Total := 0.0;
                    "DB2025_TOT".TOT[#for_i].Estado_Proceso := 0.0;
                    "DB2025_TOT".TOT[#for_i].Estado_Usuario := 0.0;
                    
                END_IF;
                
            END_FOR;
            
        END_REGION
        
    END_NETWORK
END_FUNCTION
