Primera version

This commit is contained in:
2026-07-14 10:21:52 +02:00
parent 273e2be076
commit 430e3b4db0
222 changed files with 176320 additions and 0 deletions
@@ -0,0 +1,201 @@
FUNCTION "FC2926_ZC_FF_INDV" : Void
TITLE = FC2926_ZC_FF
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion de flip flop
VAR_INPUT
Pulso1Seg : Bool; // Pulso de 1 segundo
END_VAR
VAR_IN_OUT
FF : "UDT_ZC_DISP_FF";
END_VAR
VAR CONSTANT
REPOSO : SInt := 0;
FLOP : SInt := 10;
PARO1 : SInt := 20;
FLIP : SInt := 30;
PARO2 : SInt := 40;
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2026 - 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 encargada de gestionar un dispositivo flip flop con funcion de paro entre cambios.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y calculos de tiempo de cambio entre ordenes Flip, Flop y paro.
- Adicionalmente, se gestiona tambien la posibilidad de pausar el contaje del tiempo, para evitar su reinicializacion en caso de pausa.
- En los cambios entre ´flip´ <--> ´flop´, se activa tambien la marca ´paro´ para poder gestionar los cambios y evitar golpes de ariete.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_DISP_FF_PARO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 02.06.2026 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
// ==========================================================================================================
REGION COMPROBACIONES_INICIALES
// Si no hay orden o faltan tiempos, reseteamos valores y salimos de la funcion
IF NOT #FF.Orden_Activar OR (#FF.Config_SetPointTiempoFlip <= 0 AND #FF.Config_SetPointTiempoFlop <= 0) THEN
#FF.Estado_Flip := FALSE;
#FF.Estado_Flop := FALSE;
#FF.Estado_Paro1 := FALSE;
#FF.Estado_Paro2 := FALSE;
#FF.Aux_Tiempo := 0;
#FF.Estado_Etapa := #REPOSO;
#FF.Aux_Inicializado := FALSE;
RETURN;
END_IF;
END_REGION COMPROBACIONES_INICIALES
// ==========================================================================================================
REGION INICIALIZACION
// Arranque inicial forzado a Flop si no hay estado activo
IF NOT #FF.Aux_Inicializado THEN
#FF.Estado_Etapa := #FLOP;
#FF.Aux_Inicializado := TRUE;
END_IF;
END_REGION INICIALIZACION
// ==========================================================================================================
REGION LOGICA_DE_ESTADOS
CASE #FF.Estado_Etapa OF
#FLOP:
// Salidas
#FF.Estado_Flip := FALSE;
#FF.Estado_Flop := TRUE;
#FF.Estado_Paro1 := FALSE;
#FF.Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT #FF.Orden_Pausa THEN
#FF.Aux_Tiempo += 1;
END_IF;
// Transición
IF #FF.Aux_Tiempo >= #FF.Config_SetPointTiempoFlop THEN
#FF.Aux_Tiempo := 0;
// Si no tenemos tiempo de paro configurado, saltamos a FLIP
IF #FF.Config_SetPointTiempoParo <= 0 THEN
#FF.Estado_Etapa := #FLIP;
ELSE
#FF.Estado_Etapa := #PARO1;
END_IF;
END_IF;
#PARO1:
// Salidas
#FF.Estado_Flip := FALSE;
#FF.Estado_Flop := FALSE;
#FF.Estado_Paro1 := TRUE;
#FF.Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT #FF.Orden_Pausa THEN
#FF.Aux_Tiempo += 1;
END_IF;
// Transición
IF #FF.Aux_Tiempo >= #FF.Config_SetPointTiempoParo THEN
#FF.Aux_Tiempo := 0;
#FF.Estado_Etapa := #FLIP;
END_IF;
#FLIP:
// Salidas
#FF.Estado_Flip := TRUE;
#FF.Estado_Flop := FALSE;
#FF.Estado_Paro1 := FALSE;
#FF.Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT #FF.Orden_Pausa THEN
#FF.Aux_Tiempo += 1;
END_IF;
// Transición
IF #FF.Aux_Tiempo >= #FF.Config_SetPointTiempoFlip THEN
#FF.Aux_Tiempo := 0;
// Si no tenemos tiempo de paro configurado, saltamos a FLOP
IF #FF.Config_SetPointTiempoParo <= 0 THEN
#FF.Estado_Etapa := #FLOP;
ELSE
#FF.Estado_Etapa := #PARO2;
END_IF;
END_IF;
#PARO2:
// Salidas
#FF.Estado_Flip := FALSE;
#FF.Estado_Flop := FALSE;
#FF.Estado_Paro1 := FALSE;
#FF.Estado_Paro2 := TRUE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT #FF.Orden_Pausa THEN
#FF.Aux_Tiempo += 1;
END_IF;
// Transición
IF #FF.Aux_Tiempo >= #FF.Config_SetPointTiempoParo THEN
#FF.Aux_Tiempo := 0;
#FF.Estado_Etapa := #FLOP;
END_IF;
ELSE
#FF.Estado_Etapa := #REPOSO;
END_CASE;
END_REGION LOGICA_DE_ESTADOS
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,198 @@
FUNCTION "FC2927_ZC_FF_SEC_INDV" : Void
TITLE = FC2927_FF_SEC
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion de Flip Flop secuencial
VAR_INPUT
Pulso1Seg : Bool; // Pulso 1 segundo
END_VAR
VAR_IN_OUT
FF_SEC : "UDT_ZC_DISP_FF_SEC";
END_VAR
VAR_TEMP
for_i : Int;
t_SumaFlip : Int;
END_VAR
VAR CONSTANT
REPOSO : SInt := 0;
FLOP : SInt := 10;
FLIP : SInt := 20;
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2023 - 2025
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Función encargada de gestionar un dispositivo flip flop secuencial.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y calculos de tiempo de cambio entre ordenes Flip y Flop.
- Adicionalmente, se gestiona tambien la posibilidad de pausar el contaje del tiempo, para evitar su reinicializacion en caso de pausa.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_DISP_FF_SEC` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 02.10.2023 | (ABH) | Primera version. |
| 00.00.02 | 28.11.2023 | (ABH) | Se añade condicion de activacion del Flip Flop a que algun SP sea mayor que 0. |
| 01.00.00 | 15.10.2025 | (ABH) | Se añade orden pausa para dejar de contar. Refactorizacion de logica de gestion de flips. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
// ==========================================================================================================
REGION COMPROBACIONES_INICIALES
// Suma de seguridad por FOR (Determinista)
#t_SumaFlip := 0;
FOR #for_i := 1 TO 10 DO
#t_SumaFlip += #FF_SEC.Config_SetPointTiempoFlip[#for_i];
END_FOR;
// Si no hay orden o los tiempos globales son 0, reseteamos valores y salimos de la funcion
IF NOT #FF_SEC.Orden_Activar OR (#t_SumaFlip <= 0 AND #FF_SEC.Config_SetPointTiempoFlop <= 0) THEN
#FF_SEC.Estado_SalidaFlop := FALSE;
FOR #for_i := 1 TO 10 DO
#FF_SEC.Estado_SalidaFlip[#for_i] := FALSE;
END_FOR;
#FF_SEC.Aux_Tiempo := 0;
#FF_SEC.Estado_Etapa := #REPOSO;
#FF_SEC.Aux_FlipActual := 0;
#FF_SEC.Aux_Inicializacion := FALSE;
RETURN;
END_IF;
END_REGION COMPROBACIONES_INICIALES
// ==========================================================================================================
REGION INICIALIZACION
// Arranque inicial forzado a Flop si no hay estado activo
IF NOT #FF_SEC.Aux_Inicializacion THEN
#FF_SEC.Estado_Etapa := #FLOP;
#FF_SEC.Aux_FlipActual := 0;
#FF_SEC.Aux_Inicializacion := TRUE;
END_IF;
END_REGION INICIALIZACION
// ==========================================================================================================
REGION LOGICA_DE_ESTADOS
CASE #FF_SEC.Estado_Etapa OF
#FLOP:
// Salidas
#FF_SEC.Estado_SalidaFlop := TRUE;
FOR #for_i := 1 TO 10 DO
#FF_SEC.Estado_SalidaFlip[#for_i] := FALSE;
END_FOR;
// Conteo de tiempo
IF #Pulso1Seg AND NOT #FF_SEC.Orden_Pausa THEN
#FF_SEC.Aux_Tiempo += 1;
END_IF;
// Transición
IF #FF_SEC.Aux_Tiempo >= #FF_SEC.Config_SetPointTiempoFlop THEN
#FF_SEC.Aux_Tiempo := 0;
#FF_SEC.Aux_FlipActual := 11; // Fallback por defecto
// Buscar primer Flip válido
FOR #for_i := 1 TO 10 DO
IF #FF_SEC.Config_SetPointTiempoFlip[#for_i] > 0 THEN
#FF_SEC.Aux_FlipActual := #for_i;
EXIT; // Corta el FOR en el primer acierto
END_IF;
END_FOR;
// Transición si encontró algo válido
IF #FF_SEC.Aux_FlipActual <= 10 THEN
#FF_SEC.Estado_Etapa := #FLIP;
END_IF;
END_IF;
#FLIP:
// Salidas
#FF_SEC.Estado_SalidaFlop := FALSE;
FOR #for_i := 1 TO 10 DO
IF #for_i = #FF_SEC.Aux_FlipActual THEN
#FF_SEC.Estado_SalidaFlip[#for_i] := TRUE;
ELSE
#FF_SEC.Estado_SalidaFlip[#for_i] := FALSE;
END_IF;
END_FOR;
// Conteo de tiempo
IF #Pulso1Seg AND NOT #FF_SEC.Orden_Pausa THEN
#FF_SEC.Aux_Tiempo += 1;
END_IF;
/// Transición
IF #FF_SEC.Aux_Tiempo >= #FF_SEC.Config_SetPointTiempoFlip[#FF_SEC.Aux_FlipActual] THEN
#FF_SEC.Aux_Tiempo := 0;
#FF_SEC.Aux_FlipActual += 1; // Apunta al siguiente
// Búsqueda del siguiente Flip válido
FOR #for_i := #FF_SEC.Aux_FlipActual TO 10 DO
IF #FF_SEC.Config_SetPointTiempoFlip[#for_i] > 0 THEN
#FF_SEC.Aux_FlipActual := #for_i;
EXIT;
END_IF;
// Si llega al final sin encontrar nada
IF #for_i = 10 THEN
#FF_SEC.Aux_FlipActual := 11;
END_IF;
END_FOR;
// Si rebasó el límite, vuelve al Flop
IF #FF_SEC.Aux_FlipActual > 10 THEN
#FF_SEC.Estado_Etapa := #FLOP;
END_IF;
END_IF;
ELSE
#FF_SEC.Estado_Etapa := #REPOSO;
END_CASE;
END_REGION LOGICA_DE_ESTADOS
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,78 @@
FUNCTION "FC2928_ZC_TON_INDV" : Void
TITLE = FC2928_TON
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion temporizador TON (Base 1 seg)
VAR_INPUT
Pulso1seg : Bool;
END_VAR
VAR_IN_OUT
TON : "UDT_ZC_DISP_TON";
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2022 - 2022
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Función encargada de gestionar un dispositivo TON.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y temporizacion de la activacion del temporizador.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_DISP_TON` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 02.10.2022 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
IF #TON.IN THEN
IF #TON.ET >= #TON.SP THEN
#TON.Q := TRUE;
ELSE
IF #Pulso1seg THEN
#TON.ET += 1;
END_IF;
END_IF;
ELSE
#TON.ET := 0;
#TON.Q := FALSE;
END_IF;
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,83 @@
FUNCTION "FC2929_ZC_TOF_INDV" : Void
TITLE = FC2929_TOF
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion temporizador TOF (Base 1 seg)
VAR_INPUT
Pulso1seg : Bool;
END_VAR
VAR_IN_OUT
TOF : "UDT_ZC_DISP_TOF";
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2023 - 2025
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Función encargada de gestionar un dispositivo TOF.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y temporizacion de la activacion del temporizador.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_DISP_TOF` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 02.10.2022 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
IF #TOF.IN THEN
#TOF.OldActivar := TRUE;
#TOF.Q := TRUE;
END_IF;
IF #TOF.OldActivar AND NOT #TOF.IN THEN
IF #TOF.ET <= 0 THEN
#TOF.Q := FALSE;
#TOF.OldActivar := FALSE;
ELSE
IF #Pulso1seg THEN
#TOF.ET -= 1;
END_IF;
END_IF;
ELSE
#TOF.ET := #TOF.SP;
END_IF;
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,87 @@
FUNCTION "FC2930_ZC_TONR_INDV" : Void
TITLE = FC2930_TONR
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion temporizador TONR (Base 1 seg)
VAR_INPUT
Pulso1seg : Bool;
END_VAR
VAR_IN_OUT
TONR : "UDT_ZC_DISP_TONR";
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2022 - 2025
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Función encargada de gestionar un dispositivo TONR.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y temporizacion de la activacion del temporizador.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_DISP_TONR` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 02.10.2022 | (ABH) | Primera version. |
| 01.00.01 | 13.08.2025 | (ABH) | Se modifica activacion de salida Q sin dependencia de entrada IN. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
IF #TONR.RESET THEN
#TONR.ET := 0;
#TONR.Q := FALSE;
#TONR.RESET := FALSE;
END_IF;
IF #TONR.IN AND
#TONR.ET < #TONR.SP AND
#Pulso1seg
THEN
#TONR.ET += 1;
#TONR.Q := FALSE;
END_IF;
IF #TONR.ET >= #TONR.SP THEN
#TONR.Q := TRUE;
END_IF;
#TONR.RT := #TONR.SP - #TONR.ET;
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,98 @@
FUNCTION "FC2931_ZC_TP_INDV" : Void
TITLE = FC2931_TP
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion temporizador TP (Base 1 seg)
VAR_INPUT
Pulso1Seg : Bool;
END_VAR
VAR_IN_OUT
TP : "UDT_ZC_DISP_TP";
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2022 - 2022
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Función encargada de gestionar un dispositivo TP.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y temporizacion de la activacion del temporizador.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_DISP_TP` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 02.10.2022 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
// =============================================================================
// Detección de flanco
IF #TP.IN AND NOT #TP.oldIN THEN
#TP.Q := TRUE;
END_IF;
// Actualizo valor de memoria
#TP.oldIN := #TP.IN;
// =============================================================================
// Reinicio memoria de activación cuando piedo salida y cuento tiempo
IF NOT #TP.IN THEN
#TP.oldIN := FALSE;
END_IF;
// =============================================================================
// Incremento de tiempo
IF #TP.Q
AND
#TP.ET < #TP.PT
AND
#Pulso1Seg
THEN
#TP.ET += 1;
END_IF;
// =============================================================================
// Desactivo salida cuando cumplo tiempo de retardo de salida
IF #TP.ET >= #TP.PT THEN
#TP.Q := FALSE;
#TP.ET := 0;
END_IF;
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,308 @@
FUNCTION "FC2935_ZC_PID_INDV" : Void
TITLE = FC2935_ZC_PID_INDV
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion dispositivo PID
VAR_INPUT
PV : Real; // Valor de proceso
DispManAuto : Bool; // Dispositivo Manual - Auto
DispValorActual : Real; // Dispositivo valor actual
Ack : Bool; // Acuse general
Fecha {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
Usuario : String; // Usuario actual
NumeroPID : Int; // Numero de PID
RangoSuperiorEscalado : Real; // Rango superior de valor de proceso para escalado 0-100%
RangoInferiorEscalado : Real; // Rango inferior de valor de proceso para escalado 0-100%
UsuarioActual : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_IN_OUT
PID_CompactConfig {InstructionName := 'PID_CompactConfig'; LibVersion := '1.2'} : PID_CompactConfig;
PID_CompactControlParams {InstructionName := 'PID_CompactControlParams'; LibVersion := '1.2'} : PID_CompactControlParams;
END_VAR
VAR_TEMP
t_Norm_X : LReal;
END_VAR
BEGIN
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 PID.
En ella se realizan las siguientes acciones:
- Se gestiona el estado del dispositivo
- Se gestiona la orden de activacion y activacion retardada
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 12.05.2023 | (ABH) | Primera version |
| 01.00.01 | 18.07.2023 | (ABH) | Cambio en interfaz bloque. Se pasa IN/OUT solo el PID_COMPACT. Se añade a UDT el nombre del PID. Se añade como Input el numero de PID en el array de dispositivo PID. |
| 01.00.02 | 05.09.2023 | (ABH) | Cambio en UDT PID para añadir rampa para gestion de Set Point. Se añade gestion de rampa SP PID. |
| 01.00.03 | 18.09.2023 | (ABH) | Reset de consigna auto al final de la gestion del dispositivo. Se añade trazabilidad de forzado. |
| 01.00.04 | 16.02.2024 | (ABH) | Se elimina el PID compact y la gestion de la rampa en este FC. Se separa en FC2036_PID_COMPACT para ser llamado en OB de interrupcion. |
| 01.00.05 | 13.06.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 01.00.06 | 08.01.2025 | (ABH) | Se elimina multiplexado PID. Se realiza en FC aparte. |
| 01.00.07 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 01.00.08 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. Se añade indexHMI para lista de texto en sistemas de supervision. |
*)
END_REGION DESCRIPCION
REGION TRASPASO_DE_PARAMETROS
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2035_PID".Mux.NumDispositivos := "N_MAX_DISP_PID";
// ===============================================================================================================
// TRASPASO DE PARAMETROS A PID COMPACT
// Valores regulacion
#PID_CompactControlParams.Gain := "DB2035_PID".PID[#NumeroPID].Config_Gain;
#PID_CompactControlParams.Ti := "DB2035_PID".PID[#NumeroPID].Config_Ti;
#PID_CompactControlParams.Td := "DB2035_PID".PID[#NumeroPID].Config_Td;
// Tipo Entrada
#PID_CompactConfig.InputPerOn := "DB2035_PID".PID[#NumeroPID].Config_TipoEntrada;
// Tipo de control (Directo/Inverso)
#PID_CompactConfig.InvertControl := "DB2035_PID".PID[#NumeroPID].Config_InvertirControl;
// Limites y avisos
#PID_CompactConfig.InputUpperLimit := 99999.0;
#PID_CompactConfig.InputLowerLimit := -99999.0;
#PID_CompactConfig.InputUpperWarning := 99999.0;
#PID_CompactConfig.InputLowerWarning := -99999.0;
// Valor proceso
"DB2035_PID".PID[#NumeroPID].Estado_PV := #PV;
"DB2035_PID".PID[#NumeroPID].Config_LimiteSuperiorEntrada := #RangoSuperiorEscalado;
"DB2035_PID".PID[#NumeroPID].Config_LimiteInferiorEntrada := #RangoInferiorEscalado;
"DB2035_PID".PID[#NumeroPID].Orden_Ack := #Ack;
END_REGION TRASPASO_DE_PARAMETROS
REGION MODOS_DE_TRABAJO
// ===============================================================================================================
// Selector: (0) Automatico / (1) Manual
"DB2035_PID".PID[#NumeroPID].Estado_AutoMan :=
(#DispManAuto) OR
("DB2035_PID".PID[#NumeroPID].Orden_ActivarAuto AND "DB2035_PID".PID[#NumeroPID].Orden_Forzar) OR
(NOT "DB2035_PID".PID[#NumeroPID].Orden_ActivarAuto);
// Reset señal forzado
IF NOT "DB2035_PID".PID[#NumeroPID].Orden_ActivarAuto THEN
"DB2035_PID".PID[#NumeroPID].Orden_Forzar := FALSE;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION LIMITES_SALIDA
// ===============================================================================================================
// Configuracion del limite de la salida del PID en funcion del estado Manual/Automatico
IF "DB2035_PID".PID[#NumeroPID].Estado_AutoMan THEN
// En modo manual, configuramos el limite de la salida de 0-100%, para que al manualizar el PID se pueda poner el valor que se quiera
#PID_CompactConfig.OutputUpperLimit := 100.0;
#PID_CompactConfig.OutputLowerLimit := 0.0;
ELSE
#PID_CompactConfig.OutputUpperLimit := "DB2035_PID".PID[#NumeroPID].Config_LimiteSuperiorSalida;
#PID_CompactConfig.OutputLowerLimit := "DB2035_PID".PID[#NumeroPID].Config_LimiteInferiorSalida;
END_IF;
END_REGION LIMITES_SALIDA
REGION ESTADOS
// ===============================================================================================================
// REALIMENTACION DE LAS ENTRADAS/SALIDAS DEL PID
// Estado automatico
IF NOT "DB2035_PID".PID[#NumeroPID].Estado_AutoMan THEN
"DB2035_PID".PID[#NumeroPID].Aux_SP_Manual := "DB2035_PID".PID[#NumeroPID].Estado_ValorSalida;
"DB2035_PID".PID[#NumeroPID].Orden_ConsignaForzado := "DB2035_PID".PID[#NumeroPID].Estado_ValorSalida;
ELSE
// Estado manual - Consigna forzar PID
IF NOT #DispManAuto AND "DB2035_PID".PID[#NumeroPID].Orden_Forzar THEN
"DB2035_PID".PID[#NumeroPID].Aux_SP_Manual := "DB2035_PID".PID[#NumeroPID].Orden_ConsignaForzado;
// Estado manual - Dispositivo forzado
ELSIF #DispManAuto AND NOT "DB2035_PID".PID[#NumeroPID].Orden_Forzar THEN
"DB2035_PID".PID[#NumeroPID].Aux_SP_Manual := "DB2035_PID".PID[#NumeroPID].Orden_ConsignaForzado := #DispValorActual;
ELSIF #DispManAuto AND "DB2035_PID".PID[#NumeroPID].Orden_Forzar THEN
"DB2035_PID".PID[#NumeroPID].Aux_SP_Manual := "DB2035_PID".PID[#NumeroPID].Orden_ConsignaForzado := #DispValorActual;
ELSE
"DB2035_PID".PID[#NumeroPID].Aux_SP_Manual := "DB2035_PID".PID[#NumeroPID].Orden_ConsignaForzado := #DispValorActual;
END_IF;
END_IF;
// ===============================================================================================================
// CALCULO DE VALORES EN 0-100%
// SP
#t_Norm_X := NORM_X(MIN := #RangoInferiorEscalado, VALUE := "DB2035_PID".PID[#NumeroPID].Orden_ConsignaAuto, MAX := #RangoSuperiorEscalado);
"DB2035_PID".PID[#NumeroPID].Estado_SP_0_100 := LREAL_TO_REAL(SCALE_X(MIN := 0.0, VALUE := #t_Norm_X, MAX := 100.0));
// PV
#t_Norm_X := NORM_X(MIN := #RangoInferiorEscalado, VALUE := "DB2035_PID".PID[#NumeroPID].Estado_PV, MAX := #RangoSuperiorEscalado);
"DB2035_PID".PID[#NumeroPID].Estado_PV_0_100 := LREAL_TO_REAL(SCALE_X(MIN := 0.0, VALUE := #t_Norm_X, MAX := 100.0));
// Error SP - PV
"DB2035_PID".PID[#NumeroPID].Estado_Error_SP_PV_0_100 := ABS("DB2035_PID".PID[#NumeroPID].Estado_SP_0_100 - "DB2035_PID".PID[#NumeroPID].Estado_PV_0_100);
END_REGION ESTADOS
REGION CAMBIO_MODO
// ===============================================================================================================
// Gestion modo de trabajo del PID (Manual/Automatico)
IF "DB2035_PID".PID[#NumeroPID].Estado_AutoMan THEN
"DB2035_PID".PID[#NumeroPID].Estado_Mode := 4;
ELSE
"DB2035_PID".PID[#NumeroPID].Estado_Mode := 3;
END_IF;
// Flanco para cambio de modo
"DB2035_PID".PID[#NumeroPID].Estado_ModeActivate := "DB2035_PID".PID[#NumeroPID].Estado_Mode <> "DB2035_PID".PID[#NumeroPID].Aux_oldMode;
// Valor anterior de modo para flancos
"DB2035_PID".PID[#NumeroPID].Aux_oldMode := "DB2035_PID".PID[#NumeroPID].Estado_Mode;
END_REGION CAMBIO_MODO
REGION TRAZABILIDAD
// =============================================================================================
// TRAZA CAMBIOS EN VALOR P
IF "DB2035_PID".PID[#NumeroPID].Aux_oldGain <> "DB2035_PID".PID[#NumeroPID].Config_Gain THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_18_DISP_PID_VAL",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_PID",
CodInt_2 := #NumeroPID,
CodInt_3 := "TRZ_SYS_DISP_ACC_CAM_P",
CodInt_4 := "TRZ_SYS_COM_FLECHA",
CodInt_5 := 0,
CodReal_1 := "DB2035_PID".PID[#NumeroPID].Aux_oldGain,
CodReal_2 := "DB2035_PID".PID[#NumeroPID].Config_Gain);
END_IF;
// =============================================================================================
// TRAZA CAMBIOS EN VALOR P
IF "DB2035_PID".PID[#NumeroPID].Aux_oldTi <> "DB2035_PID".PID[#NumeroPID].Config_Ti THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_18_DISP_PID_VAL",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_PID",
CodInt_2 := #NumeroPID,
CodInt_3 := "TRZ_SYS_DISP_ACC_CAM_I",
CodInt_4 := "TRZ_SYS_COM_FLECHA",
CodInt_5 := 0,
CodReal_1 := "DB2035_PID".PID[#NumeroPID].Aux_oldTi,
CodReal_2 := "DB2035_PID".PID[#NumeroPID].Config_Ti);
END_IF;
// =============================================================================================
// TRAZA CAMBIOS EN VALOR P
IF "DB2035_PID".PID[#NumeroPID].Aux_oldTd <> "DB2035_PID".PID[#NumeroPID].Config_Td THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_18_DISP_PID_VAL",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_PID",
CodInt_2 := #NumeroPID,
CodInt_3 := "TRZ_SYS_DISP_ACC_CAM_D",
CodInt_4 := "TRZ_SYS_COM_FLECHA",
CodInt_5 := 0,
CodReal_1 := "DB2035_PID".PID[#NumeroPID].Aux_oldTd,
CodReal_2 := "DB2035_PID".PID[#NumeroPID].Config_Td);
END_IF;
// =============================================================================================
// TRAZA FORZADO PID
IF "DB2035_PID".PID[#NumeroPID].Orden_Forzar AND NOT "DB2035_PID".PID[#NumeroPID].Aux_oldForzar THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_19_DISP_PID_ORD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_PID",
CodInt_2 := #NumeroPID,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF NOT "DB2035_PID".PID[#NumeroPID].Orden_Forzar AND "DB2035_PID".PID[#NumeroPID].Aux_oldForzar THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_19_DISP_PID_ORD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_PID",
CodInt_2 := #NumeroPID,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
// =============================================================================================
// Guardamos valores anteriores
"DB2035_PID".PID[#NumeroPID].Aux_oldGain := "DB2035_PID".PID[#NumeroPID].Config_Gain;
"DB2035_PID".PID[#NumeroPID].Aux_oldTi := "DB2035_PID".PID[#NumeroPID].Config_Ti;
"DB2035_PID".PID[#NumeroPID].Aux_oldTd := "DB2035_PID".PID[#NumeroPID].Config_Td;
"DB2035_PID".PID[#NumeroPID].Aux_oldForzar := "DB2035_PID".PID[#NumeroPID].Orden_Forzar;
END_REGION TRAZABILIDAD
"DB2035_PID".PID[#NumeroPID].Aux_SP_Auto := "DB2035_PID".PID[#NumeroPID].Orden_ConsignaAuto;
// =============================================================================
// Reset de orden automatica
"DB2035_PID".PID[#NumeroPID].Orden_ActivarAuto := FALSE;
"DB2035_PID".PID[#NumeroPID].Orden_ConsignaAuto := 0.0;
END_FUNCTION
@@ -0,0 +1,138 @@
FUNCTION "FC2937_ZC_MEDIA_INDV" : Void
TITLE = FC2937_ZC_MEDIA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion medias
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
END_VAR
VAR_IN_OUT
Media : "UDT_ZC_DISP_MEDIA"; // Media
END_VAR
VAR_TEMP
t_NumeroDatos : LReal;
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2025 - 2025
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Función encargada de gestionar un dispositivo Media.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y calculo de la media.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_DISP_MEDIA` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 20.03.2025 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
// ==========================================================================================================
REGION REINICIALIZACION_DE_DATOS
// Reset temporizador interno si no esta habilitada la media
IF NOT #Media.IniciarMedia THEN
#Media.auxTiempo := 0;
END_IF;
// Reinicializacion de datos al inicializar
IF #Media.Inicializar AND NOT #Media.auxInicializar THEN
#Media.auxAcumulado := 0.0;
#Media.auxIndice := 0;
#Media.auxTiempo := 0;
#Media.ValorMinimo := 9999999.0;
#Media.ValorMaximo := -9999999.0;
END_IF;
// Estado anterior
#Media.auxInicializar := #Media.Inicializar;
END_REGION REINICIALIZACION_DE_DATOS
// ==========================================================================================================
REGION TOMA_DE_VALORES
IF #Media.IniciarMedia AND #Pulso1seg THEN
// Incrementamos el indice de tiempo
#Media.auxTiempo += 1;
// En caso de superar el tiempo de muestreo, incrementamos el acumulado, reseteamos el tiempo y aumentamos el indice
IF #Media.auxTiempo >= #Media.TiempoMuestreo THEN
#Media.auxAcumulado := #Media.ValorActual + #Media.auxAcumulado;
#Media.auxIndice += 1;
#Media.auxTiempo := 0;
END_IF;
// Guardamos el valor minimo alcanzado
IF #Media.ValorActual < #Media.ValorMinimo THEN
#Media.ValorMinimo := #Media.ValorActual;
END_IF;
// Guardamos el valor maximo alcanzado
IF #Media.ValorActual > #Media.ValorMaximo THEN
#Media.ValorMaximo := #Media.ValorActual;
END_IF;
END_IF;
// Conversion de indice a REAL para calculos
#t_NumeroDatos := DINT_TO_LREAL(#Media.auxIndice);
END_REGION TOMA_DE_VALORES
// ==========================================================================================================
REGION CALCULO_MEDIA
IF #Media.auxIndice > 0 THEN
#Media.Media := #Media.auxAcumulado / #t_NumeroDatos;
ELSE
#Media.Media := 0.0;
END_IF;
END_REGION CALCULO_MEDIA
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,129 @@
FUNCTION "FC2938_ZC_MEDIA_VOL_INDV" : Void
TITLE = FC2938_ZC_MEDIA_VOL
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion de media teniendo en cuenta el volumen/caudal.
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
END_VAR
VAR_IN_OUT
Media : "UDT_ZC_DISP_MEDIA_VOLUMEN"; // Media
END_VAR
VAR_TEMP
t_NumeroDatos : LReal;
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2025 - 2025
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Función encargada de gestionar un dispositivo Media volumen.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y calculo de la media teniendo en cuenta el volumen/caudal.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_DISP_MEDIA_VOLUMEN` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 14.10.2025 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
// ==========================================================================================================
REGION REINICIALIZACION_DE_DATOS
// Reset temporizador interno si no esta habilitada la media
IF NOT #Media.IniciarMedia THEN
#Media.auxTiempo := 0;
END_IF;
// Reinicializacion de datos al inicializar
IF #Media.Inicializar AND NOT #Media.auxInicializar THEN
#Media.auxAcumulado :=
#Media.auxVolumen :=
#Media.auxValorMedia :=
#Media.Media := 0.0;
#Media.auxTiempo := 0;
#Media.ValorMinimo := 9999999.0;
#Media.ValorMaximo := -9999999.0;
END_IF;
// Estado anterior
#Media.auxInicializar := #Media.Inicializar;
END_REGION REINICIALIZACION_DE_DATOS
// ==========================================================================================================
REGION TOMA_DE_VALORES
IF #Media.IniciarMedia AND #Pulso1seg THEN
// Incrementamos el indice de tiempo
#Media.auxTiempo += 1;
// En caso de superar el tiempo de muestreo, incrementamos el acumulado, reseteamos el tiempo y aumentamos el indice
IF #Media.auxTiempo >= #Media.TiempoMuestreo THEN
#Media.auxValorMedia := (#Media.Volumen - #Media.auxVolumen) * #Media.ValorActual;
#Media.auxVolumen := #Media.Volumen;
#Media.auxAcumulado := #Media.auxValorMedia + #Media.auxAcumulado;
#Media.Media := #Media.auxAcumulado / #Media.Volumen;
#Media.auxTiempo := 0;
END_IF;
// Guardamos el valor minimo alcanzado
IF #Media.ValorActual < #Media.ValorMinimo THEN
#Media.ValorMinimo := #Media.ValorActual;
END_IF;
// Guardamos el valor maximo alcanzado
IF #Media.ValorActual > #Media.ValorMaximo THEN
#Media.ValorMaximo := #Media.ValorActual;
END_IF;
END_IF;
END_REGION TOMA_DE_VALORES
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,608 @@
FUNCTION "FC2940_ZC_TQ_INDV" : Void
TITLE = FC2940_ZC_TQ_INDV
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : HCR
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion dispositivo tanque.
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
Arranque : Bool; // Arranque
EstadoBocaHombre : Bool; // Estado boca de hombre
EstadoNivelBajo : Bool; // Estado nivel bajo
EstadoNivelAlto : Bool; // Estado nivel alto
EstadoComOK : Bool; // Estado comunicaciones Ok
ValorNivelPresion : Real; // Valor de nivel / presion
ValorTemperatura : Real; // Valor temperatura
HisteresisAltoNivelAgitacion : Real; // Histeresis superior nivel agitacion
HisteresisBajoNivelAgitacion : Real; // Histeresis inferior nivel agitacion
HisteresisAltoNivelTemp : Real; // Histeresis superior nivel temperatura
HisteresisBajoNivelTemp : Real; // Histeresis inferior nivel temperatura
TiempoTanqueVacio : Int; // Tiempo retardo tanque vacio
TiempoTanqueLLeno : Int; // Tiempo retardo tanque lleno
HabilitarFrecAgitacion : Bool; // Habilitar Frecuencia en Agitacion
NumeroTanque : Int; // Numero de tanque
END_VAR
VAR_TEMP
t_RadioCilindro : Real;
t_RadioConoInf : Real;
t_RadioActual : Real;
t_Vol1 : Real;
t_Vol2 : Real;
t_Vol3 : Real;
t_ConsignaAgitador : Real;
t_VacioLL : Bool;
t_VacioEA : Bool;
t_HistPositiva : Real;
t_HistNegativa : Real;
t_LLenoHL : Bool;
t_LlenoEA : Bool;
t_x0 : Real;
t_x1 : Real;
t_x : Real;
t_y0 : Real;
t_y1 : Real;
END_VAR
VAR CONSTANT
CONSTANTE_EMPIRICA : Real := 10.1;
CONSTANTE_PI : Real := 3.141593;
ESTADO_DESHABILITADO : Int := 0;
ESTADO_SUCIO : Int := 1;
ESTADO_LIMPIO : Int := 2;
ESTADO_PRODUCTO : Int := 3;
ESTADO_CIP : Int := 4;
END_VAR
BEGIN
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2022 - 2026
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
El bloque implementa el modelo de dispositivo "Tanque" (`TQ`) para el control integral de depósitos en procesos industriales. Centraliza la adquisición de señales de campo, la gestión de estados del tanque, el cálculo de volumen por geometría cónica-cilíndrica, el control del agitador, el control de temperatura y la supervisión de alarmas, exponiendo toda la información al sistema SCADA mediante direccionamiento multiplexado.
Funcionalidades principales:
- Gestión del estado global del tanque: deshabilitado, sucio, CIP (limpieza in-situ), limpio y con producto.
- Lectura y validación de señales digitales y analógicas: detectores de nivel bajo (`LL`), nivel alto (`HL`), boca de hombre, valor de presión y valor de temperatura.
- Cálculo de la altura de columna de líquido a partir de la presión medida mediante una constante empírica configurable.
- Cálculo del volumen diferenciando dos tramos geométricos: zona cónica inferior (con radio variable en función del ángulo del cono y radio del cilindro) y zona cilíndrica superior.
- Detección robusta de tanque vacío y tanque lleno combinando las señales de los detectores con temporizadores (`FC2928_ZC_TON_INDV`) que filtran el volumen por consignas configurables.
- Gestión de alarmas: nivel bajo, nivel alto, boca de hombre y sobrecapacidad, con agrupación configurable por cuadro para integración en SCADA.
- Control del agitador con dos modos de operación: fijo (marcha continua) o alternado por tiempos ON/OFF (`FC2926_ZC_FF_INDV`), con consigna de velocidad fija o proporcional al volumen.
- Control de temperatura en modo calefacción o refrigeración con histéresis configurable y desactivación por nivel mínimo.
- Seguimiento del producto: registro del producto activo, transición automática entre `Producto1` y `Producto2`, detección de mezcla y borrado al salir del estado PRODUCTO.
- Soporte de cambio de estado y cambio de producto desde HMI/SCADA mediante flags de orden.
- Direccionamiento multiplexado para SCADA (no indexado), con reset de índices en el primer arranque del PLC y reubicación del tanque `TQ[0]` cuando el índice vale 0.
- Contadores de tiempo en estados LIMPIO y PRODUCTO (`FC15025_ZC_CONTADOR_TIEMPO`) para trazabilidad de proceso.
> **Restricciones:** PLC serie 1200/1500.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15025_ZC_CONTADOR_TIEMPO`, `FC2928_ZC_TON_INDV`, `FC2926_ZC_FF_INDV` |
| FB | - |
| DB | `DB2040_TQ`, `DB1_SYS` |
| UDT | `ZC_DISP_TQ`, `ZC_MUX`, `ZC_TON`, `ZC_CONTADOR_TIEMPO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.01 | 26.04.2022 | (HCR) | Primera versión. |
| 01.00.02 | 09.10.2023 | (HCR) | Cambio en activación dispositivos para que actúe solamente cuando tenga producto. |
| 01.00.03 | 15.04.2024 | (ABH) | Se añade gestión multiplexado para Scada (no indexado). |
| 01.00.04 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear índices de multiplexado. Se añade movimiento de `TQ[0]` a multiplexado en caso de que el índice sea 0. |
| 01.00.05 | 24.01.2025 | (ABH) | Se modifica la lógica del multiplexado, la asignación de `oldindex` se cambia de sitio ya que de vez en cuando (dependiendo de cuándo el HMI realiza la comunicación asíncrona con el PLC) no detectaba el cambio y se machacaban valores. |
| 01.00.16 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 02.00.00 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza códigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se añade `indexHMI` para lista de texto en sistemas de supervisión. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION LOGICA_DEL_DISPOSITIVO
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2040_TQ".Mux.NumDispositivos := "N_MAX_DISP_TQ";
// Cambio de estado desde HMI/SCADA
IF "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioEstado THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueNuevo;
"DB2040_TQ".TQ[#NumeroTanque].Orden_CambioEstado := FALSE;
END_IF;
"DB2040_TQ".TQ[#NumeroTanque].Config_Habilitar := ("DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque <> #ESTADO_DESHABILITADO);
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2040_TQ".TQ[#NumeroTanque].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2040_TQ".TQ[#NumeroTanque].Config_GrupoAlarma < 0 THEN
"DB2040_TQ".TQ[#NumeroTanque].Config_GrupoAlarma := 0;
END_IF;
IF "DB2040_TQ".TQ[#NumeroTanque].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2040_TQ".TQ[#NumeroTanque].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
REGION ASIGNAR_ESTADOS
// =============================================================================
// DETECTOR BOCA DE HOMBRE
"DB2040_TQ".TQ[#NumeroTanque].Estado_BocaHombre := #EstadoBocaHombre;
// =============================================================================
// DETECTOR LL
"DB2040_TQ".TQ[#NumeroTanque].Estado_NivelBajo := #EstadoNivelBajo;
// =============================================================================
// DETECTOR HL
"DB2040_TQ".TQ[#NumeroTanque].Estado_NivelAlto := #EstadoNivelAlto;
// =============================================================================
// LECTURA VALOR NIVEL
"DB2040_TQ".TQ[#NumeroTanque].Estado_ValorNivelPresion := #ValorNivelPresion;
// =============================================================================
// LECTURA VALOR INGENERIA TEMPERATURA
"DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura := #ValorTemperatura;
END_REGION ASIGNAR_ESTADOS
REGION ALARMAS
IF "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarAlarmas THEN
// =============================================================================
// ALARMA DETECTOR NIVEL MÍNIMO
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo := NOT "DB2040_TQ".TQ[#NumeroTanque].Estado_NivelBajo;
// =============================================================================
// ALARMA DETECTOR NIVEL MÁXIMO
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto := "DB2040_TQ".TQ[#NumeroTanque].Estado_NivelAlto;
// =============================================================================
// ALARMA DETECTOR BOCA HOMBRE
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_BocaHombre := NOT "DB2040_TQ".TQ[#NumeroTanque].Estado_BocaHombre;
// =============================================================================
// ALARMA POR CAPACIDAD
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_Capacidad := "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_CapacidadTanque;
// =============================================================================
// ALARMA GENERAL
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_General := "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo OR
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto OR
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_BocaHombre OR
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_Capacidad;
ELSE
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_General := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_BocaHombre := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_Capacidad := FALSE;
END_IF;
END_REGION ALARMAS
REGION VOLUMEN
REGION ESTADO_VOLUMEN
// =============================================================================
// POSICIÓN DE ALTURA
// 0 - Altura en cono, 1 - Altura por encima del cono
"DB2040_TQ".TQ[#NumeroTanque].Estado_PocisionAltura := ("DB2040_TQ".TQ[#NumeroTanque].Estado_Altura > "DB2040_TQ".TQ[#NumeroTanque].Config_AlturaCono);
// =============================================================================
// TANQUE VACÍO DETECTOR
#t_VacioLL := "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo;
// =============================================================================
// TANQUE VACÍO GESTION TON PARA EA < VOLUMEN TANQUE VACIO
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.SP := #TiempoTanqueVacio;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.IN := ("DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen < "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenTanqueVacio);
"FC2928_ZC_TON_INDV"(Pulso1seg := #Pulso1seg,
TON := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio);
#t_VacioEA := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.Q;
"DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueVacio := #t_VacioLL OR #t_VacioEA;
// =============================================================================
// TANQUE LLENO DETECTOR
#t_LLenoHL := "DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto;
// =============================================================================
// TANQUE LLENO GESTION TON PARA EA > VOLUMEN TANQUE LLENO O CAPACIDAD
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueLleno.SP := #TiempoTanqueLLeno;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueLleno.IN := (("DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenTanqueLleno) OR ("DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_CapacidadTanque));
"FC2928_ZC_TON_INDV"(Pulso1seg := #Pulso1seg,
TON := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueLleno);
#t_LlenoEA := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueLleno.Q;
"DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueLleno := #t_LLenoHL OR #t_LlenoEA;
END_REGION ESTADO_VOLUMEN
REGION CALCULO_VOLUMEN_TANQUE
// =============================================================================
// SEÑAL EN AUTOMATICO
IF NOT "DB2040_TQ".TQ[#NumeroTanque].Estado_AutoMan THEN
// =============================================================================
// HABILITADO CALCULO DE VOLUMEN
IF "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarCalculoVol THEN
// =============================================================================
// VALOR PRESION
"DB2040_TQ".TQ[#NumeroTanque].Estado_Presion := "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorNivelPresion;
// =============================================================================
// CÁLCULO DE COLUMNA DE LÍQUIDO
"DB2040_TQ".TQ[#NumeroTanque].Estado_Altura := "DB2040_TQ".TQ[#NumeroTanque].Estado_Presion * #CONSTANTE_EMPIRICA;
// =============================================================================
// CÁLCULO DE DIMENSIONES
#t_RadioCilindro := "DB2040_TQ".TQ[#NumeroTanque].Config_DiametroCilindro / 2.0;
#t_RadioConoInf := "DB2040_TQ".TQ[#NumeroTanque].Config_DiametroConoInferior / 2.0;
#t_RadioActual := #t_RadioCilindro;
// =============================================================================
// ALTURA INFERIOR AL CONO
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura < "DB2040_TQ".TQ[#NumeroTanque].Config_AlturaCono THEN
#t_RadioActual := ("DB2040_TQ".TQ[#NumeroTanque].Estado_Altura / TAN("DB2040_TQ".TQ[#NumeroTanque].Config_AnguloCono)); //+0.13
// =============================================================================
// LÍMITE RADIO
IF #t_RadioActual > #t_RadioCilindro THEN
#t_RadioActual := #t_RadioCilindro;
END_IF;
// =============================================================================
// CÁLCULO DE VOLUMEN INFERIOR AL CONO
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura > 0 THEN
#t_Vol1 := (#t_RadioConoInf * #t_RadioConoInf) + (#t_RadioActual * #t_RadioActual) + (#t_RadioConoInf * #t_RadioActual);
#t_Vol2 := (#CONSTANTE_PI * "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura) / 3.0;
"DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := #t_Vol1 * #t_Vol2 * 1000.0;
ELSE
"DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := 0.0;
END_IF;
// =============================================================================
// ALTURA SUPERIOR AL CONO
ELSE
#t_Vol1 := (#t_RadioConoInf * #t_RadioConoInf) + (#t_RadioActual * #t_RadioActual) + (#t_RadioConoInf * #t_RadioActual);
#t_Vol2 := (#CONSTANTE_PI * "DB2040_TQ".TQ[#NumeroTanque].Estado_Altura) / 3.0;
#t_Vol3 := #CONSTANTE_PI * #t_RadioCilindro * #t_RadioCilindro * ("DB2040_TQ".TQ[#NumeroTanque].Estado_Altura - "DB2040_TQ".TQ[#NumeroTanque].Config_AlturaCono);
"DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := ((#t_Vol1 * #t_Vol2) + #t_Vol3) * 1000.0;
END_IF;
ELSE
// =============================================================================
// VALOR PRESION
"DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorNivelPresion;
END_IF;
"DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaManualVolumen := "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen;
ELSE
// =============================================================================
// ESTADO MANUAL DE EA DE NIVEL
"DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := "DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaManualVolumen;
END_IF;
END_REGION CALCULO_VOLUMEN_TANQUE
END_REGION VOLUMEN
REGION ESTADO
// =============================================================================
// ESTADO SUCIO
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO AND "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueVacio AND #EstadoComOK THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_SUCIO;
END_IF;
// =============================================================================
// ESTADO CIP
IF "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP AND NOT "DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_CIP;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP := "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP;
END_IF;
// =============================================================================
// ESTADO LIMPIO
IF NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP AND "DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP THEN
IF "DB2040_TQ".TQ[#NumeroTanque].Aux_CipOK THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_LIMPIO;
END_IF;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP := "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP;
END_IF;
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_CIP AND NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP AND NOT "DB2040_TQ".TQ[#NumeroTanque].Aux_CipOK THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_SUCIO;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP := "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP;
END_IF;
// CONTADOR TIEMPO LIMPIO
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1seg,
Iniciar := "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_LIMPIO,
Reset := "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque <> #ESTADO_LIMPIO,
Contador := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_TiempoLimpio);
// =============================================================================
// ESTADO PRODUCTO
IF "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto AND NOT "DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueVacio THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := #ESTADO_PRODUCTO;
END_IF;
// =============================================================================
// CAMBIO DE ESTADO DESDE HMI/SCADA
IF "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd1 THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 := "DB2040_TQ".TQ[#NumeroTanque].Estado_ProductoNuevo;
"DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd1 := FALSE;
END_IF;
IF "DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd2 THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 := "DB2040_TQ".TQ[#NumeroTanque].Estado_ProductoNuevo;
"DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd2 := FALSE;
END_IF;
// =============================================================================
// PRODUCTOS
IF ("DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO) THEN
IF "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto AND NOT "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenProducto THEN
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 = 0 AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 <> 0 THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 := "DB2040_TQ".TQ[#NumeroTanque].Orden_TipoProducto;
END_IF;
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 = 0 THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 := "DB2040_TQ".TQ[#NumeroTanque].Orden_TipoProducto;
END_IF;
END_IF;
END_IF;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenProducto := "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto;
IF NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_Producto OR NOT ("DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO) THEN
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenProducto := FALSE;
END_IF;
"DB2040_TQ".TQ[#NumeroTanque].Orden_Producto := FALSE;
// CONTADOR TIEMPO PRODUCTO
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1seg,
Iniciar := "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO,
Reset := "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque <> #ESTADO_PRODUCTO,
Contador := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_TiempoProducto);
// =============================================================================
// TANQUE MEZCLA
"DB2040_TQ".TQ[#NumeroTanque].Estado_Mezcla := "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 <> 0 AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 <> 0 AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 <> "DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2;
// =============================================================================
// BORRADO DE PRODUCTOS
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque <> #ESTADO_PRODUCTO THEN
"DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 := 0;
"DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 := 0;
END_IF;
END_REGION ESTADO
REGION AGITADOR
"DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Orden_Activar := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarFrecuenciaAgitacion := #HabilitarFrecAgitacion;
// =============================================================================
// TANQUE CON AGITADOR
// IF "DB2039_TQ".TQ[N_Tanque].Config_HabilitarAgitador AND NOT "DB2039_TQ".TQ[N_Tanque].Orden_ParoControlAgitador THEN
IF "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarAgitador THEN
// =============================================================================
// SE AÑADE 50 LITROS POR ENCIMA PARA EMPEZAR A AGITAR
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlAgitador + #HisteresisAltoNivelAgitacion THEN
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador := TRUE;
END_IF;
// =============================================================================
//PARO AGITACIÓN POR NIVEL
IF "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen < "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlAgitador - #HisteresisBajoNivelAgitacion THEN
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador := FALSE;
END_IF;
// =============================================================================
//TIPO DE AGITACIÓN POR TIEMPO
IF "DB2040_TQ".TQ[#NumeroTanque].Config_TipoAgitacionTiempo = 1 THEN
"DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Config_SetPointTiempoFlip := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONConsigna;
"DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Config_SetPointTiempoFlop := "DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFConsigna;
"DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Orden_Pausa := "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlAgitador;
"DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Orden_Activar := "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador;
"FC2926_ZC_FF_INDV"(Pulso1Seg := #Pulso1seg,
FF := "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador);
IF "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Estado_Flip THEN
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Aux_Tiempo;
ELSE
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := 0;
END_IF;
IF "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Estado_Flop THEN
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := "DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Aux_Tiempo;
ELSE
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := 0;
END_IF;
ELSE
// =============================================================================
// TIPO DE AGITACIÓN FIJO
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := 0;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := 0;
END_IF;
// =============================================================================
//ORDEN ACTIVAR AGITADOR
"DB2040_TQ".TQ[#NumeroTanque].Orden_Agitacion :=
"DB2040_TQ".TQ[#NumeroTanque].FlipAgitador.Estado_Flip AND
NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlAgitador AND
"DB2040_TQ".TQ[#NumeroTanque].Estado_BocaHombre AND
"DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO;
// =============================================================================
//FRECUENCIA DE AGITADOR
IF "DB2040_TQ".TQ[#NumeroTanque].Orden_Agitacion AND "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarFrecuenciaAgitacion THEN
IF "DB2040_TQ".TQ[#NumeroTanque].Config_TipoAgitacionVelocidad = 0 THEN
// =============================================================================
//VELOCIDAD FIJA
"DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := "DB2040_TQ".TQ[#NumeroTanque].Config_VelocidadFijaAgitador;
ELSE
// =============================================================================
//VELOCIDAD POR NIVEL
#t_x0 := "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlAgitador;
#t_x1 := "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMaxControlAgitador;
#t_x := "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen;
#t_y0 := "DB2040_TQ".TQ[#NumeroTanque].Config_VelocidadMinAgitador;
#t_y1 := "DB2040_TQ".TQ[#NumeroTanque].Config_VelocidadMaxAgitador;
#t_ConsignaAgitador := #t_y0 + (#t_y1 - #t_y0) * ((#t_x - #t_x0) / (#t_x1 - #t_x0));
IF #t_ConsignaAgitador > #t_y1 THEN
#t_ConsignaAgitador := #t_y1;
END_IF;
IF #t_ConsignaAgitador < #t_y0 THEN
#t_ConsignaAgitador := #t_y0;
END_IF;
"DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := #t_ConsignaAgitador;
END_IF;
ELSE
"DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := 0.0;
END_IF;
ELSE
"DB2040_TQ".TQ[#NumeroTanque].Orden_Agitacion := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := 0;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := 0;
"DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := 0.0;
END_IF;
END_REGION AGITADOR
REGION VALVULA
// =============================================================================
// HABILITAR CONTROL DE TEMPERATURA
IF "DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarControlTemp AND NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlTemp AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO THEN
#t_HistPositiva := "DB2040_TQ".TQ[#NumeroTanque].Config_ConsignaTemperatura + "DB2040_TQ".TQ[#NumeroTanque].Config_HisteresisTemperatura;
#t_HistNegativa := "DB2040_TQ".TQ[#NumeroTanque].Config_ConsignaTemperatura - "DB2040_TQ".TQ[#NumeroTanque].Config_HisteresisTemperatura;
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen > "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlTemp + #HisteresisAltoNivelTemp THEN
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura := TRUE;
// =============================================================================
// CONTROL DE TEMPERATURA ENFRIAR
IF "DB2040_TQ".TQ[#NumeroTanque].Config_TipoControlTemperatura THEN
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura > "DB2040_TQ".TQ[#NumeroTanque].Config_ConsignaTemperatura THEN
"DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := TRUE;
END_IF;
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura < #t_HistNegativa THEN
"DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
END_IF;
// =============================================================================
// CONTROL DE TEMPERATURA CALENTAR
ELSE
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura < "DB2040_TQ".TQ[#NumeroTanque].Config_ConsignaTemperatura THEN
"DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := TRUE;
END_IF;
IF "DB2040_TQ".TQ[#NumeroTanque].Estado_ValorTemperatura > #t_HistPositiva THEN
"DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
END_IF;
END_IF;
END_IF;
// =============================================================================
//PARAO CONTROL DE TEMPERATURA POR NIVEL
IF "DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura AND "DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen < "DB2040_TQ".TQ[#NumeroTanque].Config_VolumenMinControlTemp - #HisteresisBajoNivelTemp THEN
"DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura := FALSE;
END_IF;
ELSE
"DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
END_IF;
// =============================================================================
// VISIBILIDAD CONSIGNA DE TEMPERATURA
"DB2040_TQ".TQ[#NumeroTanque].Vis_ConsignaTemperatura :=
"DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarControlTemp AND
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura AND
NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_CIP AND ("DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque = #ESTADO_PRODUCTO) AND
NOT "DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlTemp;
END_REGION VALVULA
ELSE
// =============================================================================
// DISPOSITIVO DESHABILITADO
"DB2040_TQ".TQ[#NumeroTanque].Orden_Temperatura := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Orden_Agitacion := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Orden_ConsignaAgitador := 0.0;
"DB2040_TQ".TQ[#NumeroTanque].Orden_CIP := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Orden_Producto := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Orden_CambioEstado := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd1 := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Orden_CambioProd2 := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemEstadoCIP := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenProducto := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenTemperatura := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Aux_MemOrdenAgitador := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Estado_TanqueVacio := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Estado_PocisionAltura := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Estado_NivelBajo := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Estado_NivelAlto := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Estado_BocaHombre := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Estado_Tanque := 0; // Deshabilitado
"DB2040_TQ".TQ[#NumeroTanque].Estado_Producto1 := 0;
"DB2040_TQ".TQ[#NumeroTanque].Estado_Producto2 := 0;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelBajo := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_NivelAlto := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_BocaHombre := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_Capacidad := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Alarmas_General := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Estado_Volumen := 0.0;
"DB2040_TQ".TQ[#NumeroTanque].Estado_Presion := 0.0;
"DB2040_TQ".TQ[#NumeroTanque].Estado_Altura := 0.0;
"DB2040_TQ".TQ[#NumeroTanque].Config_HabilitarControlTemp := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorONActual := 0;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_AgitadorOFFActual := 0;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.ET := 0;
"DB2040_TQ".TQ[#NumeroTanque].Tiempo_RetTanqueVacio.Q := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlAgitador := FALSE;
"DB2040_TQ".TQ[#NumeroTanque].Orden_ParoControlTemp := FALSE;
END_IF;
END_REGION LOGICA_DEL_DISPOSITIVO
END_FUNCTION