Cambio de formato de exportacion a S7dcl.

This commit is contained in:
2026-07-20 16:03:44 +02:00
parent 350bcb6b01
commit d551a132c0
714 changed files with 44789 additions and 30182 deletions
@@ -0,0 +1,383 @@
{
S7_Author := "HCR";
S7_BlockNumber := "15149";
S7_BlockTitle := "MLC_3cn";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION_BLOCK "FB15049_ZC_AIRE_ESTERIL"
VAR_INPUT
{ S7_MLC := "MLC_33z" }
Pulso1Seg : Bool;
HabilitarMantenimiento : Bool;
AlarmaFalloVapor : Bool;
{ S7_MLC := "MLC_Xw" }
FechaHora : DTL;
{ S7_MLC := "MLC_4TG" }
TempEsterilAlcanzada : Bool;
{ S7_MLC := "MLC_3r4" }
SP_TiempoEsterilizacion : Int;
{ S7_MLC := "MLC_3XU" }
SP_TiempoEnfriamiento : Int;
SP_NumeroMaximoEsterilizacion : Int;
{ S7_MLC := "MLC_zF" }
SP_DiasMaximoFiltroEsteril : Int;
{ S7_MLC := "MLC_WZ" }
ProcesoID : Int;
END_VAR
VAR_INPUT RETAIN
{ S7_MLC := "MLC_3cm" }
Usuario : String[25];
END_VAR
VAR_INPUT
MinutoAlarma : USInt;
SegundoAlarma : USInt;
END_VAR
VAR_IN_OUT
ParoMantenimiento : Bool;
AvisoDiasEsterilizacion : Bool;
AlarmaDiasEsterilizacion : Bool;
END_VAR
VAR
{ S7_MLC := "MLC_4jQ" }
s_Habilitar : Bool;
{ S7_MLC := "MLC_ZU" }
s_FiltroEsteril : Bool;
END_VAR
VAR RETAIN
{ S7_MLC := "MLC_3qw" }
s_Etapa : Int;
{ S7_MLC := "MLC_m7" }
s_NumeroEsterilizaciones : Int;
END_VAR
VAR
{ S7_MLC := "MLC_3gR" }
S_NumeroDiasEsterilizacion : Int;
END_VAR
VAR RETAIN
{
S7_MLC := "MLC_4hW";
S7_Setpoint := "False"
}
s_TiempoEtapa : _.UDT_ZC_PRO_TIEMPO_ETAPA;
END_VAR
VAR
Pulsadores : STRUCT
{ S7_Setpoint := "False" }
Habilitar : _.UDT_ZC_PULSADOR;
{ S7_Setpoint := "False" }
Deshabilitar : _.UDT_ZC_PULSADOR;
{ S7_Setpoint := "False" }
Mantenimiento : _.UDT_ZC_PULSADOR;
END_STRUCT;
END_VAR
VAR RETAIN
TiempoEsterelizado : _.UDT_ZC_CONTADOR_TIEMPO;
END_VAR
VAR
{
S7_Access := "ReadOnly := External";
S7_MLC := "MLC_RL";
S7_Visibility := "Hidden := External"
}
s_PasswordCalculado : UInt;
{ S7_MLC := "MLC_3mQ" }
s_PasswordHMI : UInt;
{ S7_Setpoint := "True" }
s_MostrarAlarmaMto : Bool;
{ S7_Setpoint := "True" }
s_oldMostrarAlarmaMto : Bool;
END_VAR
VAR_TEMP
{ S7_MLC := "MLC_ps" }
t_ResetContador : Bool;
t_DiasAvisos : Int;
END_VAR
VAR CONSTANT
REPOSO : Int := 0;
ESTERILIZANDO : Int := 10;
ESTERILIZADO : Int := 20;
ENFRIAMIENTO : Int := 30;
END_VAR
{ S7_Language := "SCL" }
NETWORK
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2024 - 202
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
Función principal para la gestión integral y secuencial del dispositivo del tanque de aire estéril.
> **Restricciones:** Ninguna.
- Evaluar y gestionar las órdenes temporizadas provenientes de los pulsadores del HMI (`Habilitar`, `Deshabilitar`, `Mantenimiento`) mediante la invocación de `FC15026_ZC_PULSADOR`.
- Ejecutar la máquina de estados principal para controlar el ciclo de vida y las fases operativas del sistema: `REPOSO`, `ESTERILIZANDO`, `ENFRIAMIENTO` y `ESTERILIZADO`.
- Contabilizar el tiempo de permanencia en estado estéril utilizando `FC15025_ZC_CONTADOR_TIEMPO`, llevando el registro acumulado de días de operación del filtro.
- Monitorizar dinámicamente los tiempos de cada etapa (`s_TiempoEtapa`) y validar las transiciones de estado en función de la temperatura (`TempEsterilAlcanzada`) o la detección de fallos (`AlarmaFalloVapor`).
- Revocar automáticamente el estado de filtro estéril (`s_FiltroEsteril`) al superar el límite máximo de días configurado en `SP_DiasMaximoFiltroEsteril`.
- Procesar las rutinas de parada por mantenimiento preventivo, exigiendo validación de seguridad mediante contraseña (`s_PasswordHMI` vs `s_PasswordCalculado`) una vez alcanzado el límite de esterilizaciones (`SP_NumeroMaximoEsterilizacion`).
- Calcular y emitir señales preventivas de aviso (`AvisoDiasEsterilizacion`) y alarma crítica (`AlarmaDiasEsterilizacion`) según la caducidad operativa del filtro estéril.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15025_ZC_CONTADOR_TIEMPO`, `FC15026_ZC_PULSADOR` |
| FB | - |
| DB | `DB1_SYS` |
| UDT | `UDT_ZC_CONTADOR_TIEMPO`, `UDT_ZC_PRO_TIEMPO_ETAPA`, `UDT_ZC_PULSADOR` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 15.10.2024 | (HCR) | Primera version. |
| 01.00.01 | 28.01.2025 | (ABH) | Se cambia organizacion de variables y nombres para estandarizacion. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION PULSADORES
// Permiso pulsadores
#Pulsadores.Habilitar.Permiso := NOT #s_Habilitar;
#Pulsadores.Deshabilitar.Permiso := #s_Habilitar;
#Pulsadores.Mantenimiento.Permiso := #ParoMantenimiento;
// Gestion pulsadores
"FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
SP_Tiempo := 2,
TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
TrazaIdProceso:=#ProcesoID,
TrazaIdPulsador:="TRZ_SYS_PUL_AE_HAB",
Usuario := #Usuario,
FechaHoraActual := #FechaHora,
Pulsador := #Pulsadores.Habilitar);
"FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
SP_Tiempo := 2,
TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
TrazaIdProceso:=#ProcesoID,
TrazaIdPulsador:="TRZ_SYS_PUL_AE_DES",
Usuario := #Usuario,
FechaHoraActual := #FechaHora,
Pulsador := #Pulsadores.Deshabilitar);
"FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
SP_Tiempo := 2,
TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
TrazaIdProceso:=#ProcesoID,
TrazaIdPulsador:="TRZ_SYS_PUL_AE_MNT",
Usuario := #Usuario,
FechaHoraActual := #FechaHora,
Pulsador := #Pulsadores.Mantenimiento);
// Orden habilitar aire esteril
IF #Pulsadores.Habilitar.Plc THEN
#s_Habilitar := TRUE;
ELSIF #Pulsadores.Deshabilitar.Plc THEN
#s_Habilitar := FALSE;
END_IF;
END_REGION PULSADORES
// ==========================================================================================================
REGION SECUENCIA_FILTRO
CASE #s_Etapa OF
// =============================================================================
#REPOSO:
IF #s_Habilitar THEN
#s_Etapa := #ESTERILIZADO;
#s_TiempoEtapa.Actual := 0;
END_IF;
// =============================================================================
#ESTERILIZANDO:
#s_TiempoEtapa.SP := #SP_TiempoEsterilizacion;
IF NOT #s_Habilitar THEN
#s_Etapa := #REPOSO;
END_IF;
IF #AlarmaFalloVapor THEN
#s_Etapa := #REPOSO;
END_IF;
IF #s_TiempoEtapa.Cumplido THEN
#s_Etapa := #ENFRIAMIENTO;
#s_TiempoEtapa.Actual := 0;
#S_NumeroDiasEsterilizacion := 0;
#s_NumeroEsterilizaciones += 1;
#s_FiltroEsteril := TRUE;
END_IF;
IF #TempEsterilAlcanzada THEN
#s_TiempoEtapa.Activar := TRUE;
ELSE
#s_TiempoEtapa.Activar := FALSE;
#s_TiempoEtapa.Actual := 0;
END_IF;
// =============================================================================
#ENFRIAMIENTO:
#s_TiempoEtapa.SP := #SP_TiempoEnfriamiento;
#s_TiempoEtapa.Activar := TRUE;
IF NOT #s_Habilitar THEN
#s_Etapa := #REPOSO;
END_IF;
IF #s_TiempoEtapa.Cumplido THEN
#s_Etapa := #ESTERILIZADO;
#s_TiempoEtapa.Actual := 0;
END_IF;
// =============================================================================
#ESTERILIZADO:
#s_TiempoEtapa.SP := 5;
IF NOT #s_Habilitar THEN
#s_Etapa := #REPOSO;
END_IF;
IF #s_TiempoEtapa.Cumplido THEN
#s_Etapa := #ESTERILIZANDO;
#s_TiempoEtapa.Actual := 0;
END_IF;
IF #TempEsterilAlcanzada AND NOT #s_FiltroEsteril THEN
#s_TiempoEtapa.Activar := TRUE;
ELSE
#s_TiempoEtapa.Activar := FALSE;
#s_TiempoEtapa.Actual := 0;
END_IF;
ELSE
#s_Etapa := #REPOSO;
END_CASE;
END_REGION SECUENCIA_FILTRO
// ==========================================================================================================
REGION TIEMPO_ESTERILIZADO
// Incrementar dias de filtro #ESTERILIZADO
IF #TiempoEsterelizado.Hora >= 24 THEN
#t_ResetContador := TRUE;
#S_NumeroDiasEsterilizacion += 1;
END_IF;
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg:=#Pulso1Seg,
Iniciar:=#Pulso1Seg AND (#s_Etapa = #ESTERILIZADO),
Reset:=#t_ResetContador,
Contador:=#TiempoEsterelizado);
// Quitar estado filtro esteril
IF #S_NumeroDiasEsterilizacion >= #SP_DiasMaximoFiltroEsteril THEN
#s_FiltroEsteril := FALSE;
END_IF;
END_REGION TIEMPO_ESTERILIZADO
// ==========================================================================================================
REGION TIEMPO_ETAPA
// Proceso en estado parado
IF #s_Etapa = #REPOSO THEN
#s_TiempoEtapa.Actual := 0;
#s_TiempoEtapa.Activar := FALSE;
END_IF;
// Temporizador activo
IF #s_TiempoEtapa.Activar THEN
IF #Pulso1Seg
AND
(#s_TiempoEtapa.Actual < #s_TiempoEtapa.SP)
THEN
#s_TiempoEtapa.Actual += 1;
END_IF;
// Reiniciar tiempo paso cumplido para esperar condicion
#s_TiempoEtapa.Cumplido := FALSE;
// Tiempo paso cumplido
IF #s_TiempoEtapa.Actual >= #s_TiempoEtapa.SP AND #s_TiempoEtapa.Activar THEN
#s_TiempoEtapa.Cumplido := TRUE;
END_IF;
// Tiempo restante
#s_TiempoEtapa.Restante := #s_TiempoEtapa.SP - #s_TiempoEtapa.Actual;
END_IF;
// Tiempo restante
#s_TiempoEtapa.Restante := #s_TiempoEtapa.SP - #s_TiempoEtapa.Actual;
END_REGION TIEMPO_ETAPA
// ==========================================================================================================
REGION MANTENIMIENTO
// Flanco mostrar cartel de mantenieminto
IF #HabilitarMantenimiento THEN
#s_MostrarAlarmaMto := (#FechaHora.MINUTE = #MinutoAlarma) AND (#FechaHora.SECOND = #SegundoAlarma) AND (#s_NumeroEsterilizaciones >= #SP_NumeroMaximoEsterilizacion);
IF #s_MostrarAlarmaMto AND NOT #s_oldMostrarAlarmaMto THEN
#ParoMantenimiento := TRUE;
#s_PasswordCalculado :=
#s_PasswordHMI := 0;
END_IF;
// Pulsador chequear contraseña de mantenimiento
IF #Pulsadores.Mantenimiento.Plc THEN
#s_PasswordCalculado := #FechaHora.YEAR + #FechaHora.MONTH + #FechaHora.DAY;
IF #s_PasswordCalculado = #s_PasswordHMI THEN
#s_NumeroEsterilizaciones := 0;
#S_NumeroDiasEsterilizacion := 0;
END_IF;
#s_PasswordCalculado :=
#s_PasswordHMI := 0;
#ParoMantenimiento := FALSE;
END_IF;
ELSE
#ParoMantenimiento := FALSE;
END_IF;
#s_oldMostrarAlarmaMto := #s_MostrarAlarmaMto;
END_REGION MANTENIMIENTO
// ==========================================================================================================
REGION ALARMAS
// Aviso un dia antes
#t_DiasAvisos := #SP_DiasMaximoFiltroEsteril - 1;
IF #t_DiasAvisos < 0 THEN
#t_DiasAvisos := 0;
END_IF;
// Alarmas por dia de esterilizacion
#AlarmaDiasEsterilizacion := (#S_NumeroDiasEsterilizacion >= #SP_DiasMaximoFiltroEsteril);
#AvisoDiasEsterilizacion := (#S_NumeroDiasEsterilizacion >= #t_DiasAvisos AND NOT #AlarmaDiasEsterilizacion);
END_REGION ALARMAS
// ==========================================================================================================
REGION NUMERO_ESTERILIZACIONES
IF #s_NumeroEsterilizaciones = 0 THEN
#s_NumeroEsterilizaciones += 1;
END_IF;
END_REGION NUMERO_ESTERILIZACIONES
END_NETWORK
END_FUNCTION_BLOCK
@@ -0,0 +1,37 @@
MultiLingualTexts:
- id: MLC_3cn
es-ES: FB15049_ZC_AIRE_ESTERIL
- id: MLC_33z
es-ES: Pulso 1 Segundo
- id: MLC_Xw
es-ES: Fecha y hora actual
- id: MLC_4TG
es-ES: Estado temperatura de esterilizacion alcanzada
- id: MLC_3r4
es-ES: SP_Tiempo de esterilizacion
- id: MLC_3XU
es-ES: SP_Tiempo de enfriamiento
- id: MLC_zF
es-ES: SP_Dias Maximo Filtro Esteril
- id: MLC_WZ
es-ES: ID Proceso para trazabilidad
- id: MLC_3cm
es-ES: Usuario actual
- id: MLC_4jQ
es-ES: Estado habilitado
- id: MLC_ZU
es-ES: Estado filtro esteril
- id: MLC_3qw
es-ES: Etapa actual
- id: MLC_m7
es-ES: Numero de esterilizaciones
- id: MLC_3gR
es-ES: Numerpo de dias con filtro esterilizado
- id: MLC_4hW
es-ES: Tiempo de etapa
- id: MLC_RL
es-ES: Contraseña calculada de mantenimiento
- id: MLC_3mQ
es-ES: Contraseña calculada de mantenimiento
- id: MLC_ps
es-ES: Tempoeral reset contador de horas
@@ -1,354 +0,0 @@
FUNCTION_BLOCK "FB15049_ZC_AIRE_ESTERIL"
TITLE = FB15049_ZC_AIRE_ESTERIL
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : HCR
FAMILY : ZeusControl
VERSION : 1.0
VAR_INPUT
Pulso1Seg : Bool; // Pulso 1 Segundo
HabilitarMantenimiento : Bool;
AlarmaFalloVapor : Bool;
FechaHora {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
TempEsterilAlcanzada : Bool; // Estado temperatura de esterilizacion alcanzada
SP_TiempoEsterilizacion : Int; // SP_Tiempo de esterilizacion
SP_TiempoEnfriamiento : Int; // SP_Tiempo de enfriamiento
SP_NumeroMaximoEsterilizacion : Int;
SP_DiasMaximoFiltroEsteril : Int; // SP_Dias Maximo Filtro Esteril
ProcesoID : Int; // ID Proceso para trazabilidad
END_VAR
VAR_INPUT RETAIN
Usuario : String[25]; // Usuario actual
END_VAR
VAR_INPUT
MinutoAlarma : USInt;
SegundoAlarma : USInt;
END_VAR
VAR_IN_OUT
ParoMantenimiento : Bool;
AvisoDiasEsterilizacion : Bool;
AlarmaDiasEsterilizacion : Bool;
END_VAR
VAR
s_Habilitar : Bool; // Estado habilitado
s_FiltroEsteril : Bool; // Estado filtro esteril
END_VAR
VAR RETAIN
s_Etapa : Int; // Etapa actual
s_NumeroEsterilizaciones : Int; // Numero de esterilizaciones
END_VAR
VAR
S_NumeroDiasEsterilizacion : Int; // Numerpo de dias con filtro esterilizado
END_VAR
VAR RETAIN
s_TiempoEtapa { S7_SetPoint := 'False'} : "UDT_ZC_PRO_TIEMPO_ETAPA"; // Tiempo de etapa
END_VAR
VAR
Pulsadores : Struct
Habilitar { S7_SetPoint := 'False'} : "UDT_ZC_PULSADOR";
Deshabilitar { S7_SetPoint := 'False'} : "UDT_ZC_PULSADOR";
Mantenimiento { S7_SetPoint := 'False'} : "UDT_ZC_PULSADOR";
END_STRUCT;
END_VAR
VAR RETAIN
TiempoEsterelizado : "UDT_ZC_CONTADOR_TIEMPO";
END_VAR
VAR
s_PasswordCalculado { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt; // Contraseña calculada de mantenimiento
s_PasswordHMI : UInt; // Contraseña calculada de mantenimiento
s_MostrarAlarmaMto { S7_SetPoint := 'True'} : Bool;
s_oldMostrarAlarmaMto { S7_SetPoint := 'True'} : Bool;
END_VAR
VAR_TEMP
t_ResetContador : Bool; // Tempoeral reset contador de horas
t_DiasAvisos : Int;
END_VAR
VAR CONSTANT
REPOSO : Int := 0;
ESTERILIZANDO : Int := 10;
ESTERILIZADO : Int := 20;
ENFRIAMIENTO : Int := 30;
END_VAR
BEGIN
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2024 - 202
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
Función principal para la gestión integral y secuencial del dispositivo del tanque de aire estéril.
> **Restricciones:** Ninguna.
- Evaluar y gestionar las órdenes temporizadas provenientes de los pulsadores del HMI (`Habilitar`, `Deshabilitar`, `Mantenimiento`) mediante la invocación de `FC15026_ZC_PULSADOR`.
- Ejecutar la máquina de estados principal para controlar el ciclo de vida y las fases operativas del sistema: `REPOSO`, `ESTERILIZANDO`, `ENFRIAMIENTO` y `ESTERILIZADO`.
- Contabilizar el tiempo de permanencia en estado estéril utilizando `FC15025_ZC_CONTADOR_TIEMPO`, llevando el registro acumulado de días de operación del filtro.
- Monitorizar dinámicamente los tiempos de cada etapa (`s_TiempoEtapa`) y validar las transiciones de estado en función de la temperatura (`TempEsterilAlcanzada`) o la detección de fallos (`AlarmaFalloVapor`).
- Revocar automáticamente el estado de filtro estéril (`s_FiltroEsteril`) al superar el límite máximo de días configurado en `SP_DiasMaximoFiltroEsteril`.
- Procesar las rutinas de parada por mantenimiento preventivo, exigiendo validación de seguridad mediante contraseña (`s_PasswordHMI` vs `s_PasswordCalculado`) una vez alcanzado el límite de esterilizaciones (`SP_NumeroMaximoEsterilizacion`).
- Calcular y emitir señales preventivas de aviso (`AvisoDiasEsterilizacion`) y alarma crítica (`AlarmaDiasEsterilizacion`) según la caducidad operativa del filtro estéril.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15025_ZC_CONTADOR_TIEMPO`, `FC15026_ZC_PULSADOR` |
| FB | - |
| DB | `DB1_SYS` |
| UDT | `UDT_ZC_CONTADOR_TIEMPO`, `UDT_ZC_PRO_TIEMPO_ETAPA`, `UDT_ZC_PULSADOR` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 15.10.2024 | (HCR) | Primera version. |
| 01.00.01 | 28.01.2025 | (ABH) | Se cambia organizacion de variables y nombres para estandarizacion. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION PULSADORES
// Permiso pulsadores
#Pulsadores.Habilitar.Permiso := NOT #s_Habilitar;
#Pulsadores.Deshabilitar.Permiso := #s_Habilitar;
#Pulsadores.Mantenimiento.Permiso := #ParoMantenimiento;
// Gestion pulsadores
"FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
SP_Tiempo := 2,
TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
TrazaIdProceso:=#ProcesoID,
TrazaIdPulsador:="TRZ_SYS_PUL_AE_HAB",
Usuario := #Usuario,
FechaHoraActual := #FechaHora,
Pulsador := #Pulsadores.Habilitar);
"FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
SP_Tiempo := 2,
TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
TrazaIdProceso:=#ProcesoID,
TrazaIdPulsador:="TRZ_SYS_PUL_AE_DES",
Usuario := #Usuario,
FechaHoraActual := #FechaHora,
Pulsador := #Pulsadores.Deshabilitar);
"FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
SP_Tiempo := 2,
TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
TrazaIdProceso:=#ProcesoID,
TrazaIdPulsador:="TRZ_SYS_PUL_AE_MNT",
Usuario := #Usuario,
FechaHoraActual := #FechaHora,
Pulsador := #Pulsadores.Mantenimiento);
// Orden habilitar aire esteril
IF #Pulsadores.Habilitar.Plc THEN
#s_Habilitar := TRUE;
ELSIF #Pulsadores.Deshabilitar.Plc THEN
#s_Habilitar := FALSE;
END_IF;
END_REGION PULSADORES
// ==========================================================================================================
REGION SECUENCIA_FILTRO
CASE #s_Etapa OF
// =============================================================================
#REPOSO:
IF #s_Habilitar THEN
#s_Etapa := #ESTERILIZADO;
#s_TiempoEtapa.Actual := 0;
END_IF;
// =============================================================================
#ESTERILIZANDO:
#s_TiempoEtapa.SP := #SP_TiempoEsterilizacion;
IF NOT #s_Habilitar THEN
#s_Etapa := #REPOSO;
END_IF;
IF #AlarmaFalloVapor THEN
#s_Etapa := #REPOSO;
END_IF;
IF #s_TiempoEtapa.Cumplido THEN
#s_Etapa := #ENFRIAMIENTO;
#s_TiempoEtapa.Actual := 0;
#S_NumeroDiasEsterilizacion := 0;
#s_NumeroEsterilizaciones += 1;
#s_FiltroEsteril := TRUE;
END_IF;
IF #TempEsterilAlcanzada THEN
#s_TiempoEtapa.Activar := TRUE;
ELSE
#s_TiempoEtapa.Activar := FALSE;
#s_TiempoEtapa.Actual := 0;
END_IF;
// =============================================================================
#ENFRIAMIENTO:
#s_TiempoEtapa.SP := #SP_TiempoEnfriamiento;
#s_TiempoEtapa.Activar := TRUE;
IF NOT #s_Habilitar THEN
#s_Etapa := #REPOSO;
END_IF;
IF #s_TiempoEtapa.Cumplido THEN
#s_Etapa := #ESTERILIZADO;
#s_TiempoEtapa.Actual := 0;
END_IF;
// =============================================================================
#ESTERILIZADO:
#s_TiempoEtapa.SP := 5;
IF NOT #s_Habilitar THEN
#s_Etapa := #REPOSO;
END_IF;
IF #s_TiempoEtapa.Cumplido THEN
#s_Etapa := #ESTERILIZANDO;
#s_TiempoEtapa.Actual := 0;
END_IF;
IF #TempEsterilAlcanzada AND NOT #s_FiltroEsteril THEN
#s_TiempoEtapa.Activar := TRUE;
ELSE
#s_TiempoEtapa.Activar := FALSE;
#s_TiempoEtapa.Actual := 0;
END_IF;
ELSE
#s_Etapa := #REPOSO;
END_CASE;
END_REGION SECUENCIA_FILTRO
// ==========================================================================================================
REGION TIEMPO_ESTERILIZADO
// Incrementar dias de filtro #ESTERILIZADO
IF #TiempoEsterelizado.Hora >= 24 THEN
#t_ResetContador := TRUE;
#S_NumeroDiasEsterilizacion += 1;
END_IF;
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg:=#Pulso1Seg,
Iniciar:=#Pulso1Seg AND (#s_Etapa = #ESTERILIZADO),
Reset:=#t_ResetContador,
Contador:=#TiempoEsterelizado);
// Quitar estado filtro esteril
IF #S_NumeroDiasEsterilizacion >= #SP_DiasMaximoFiltroEsteril THEN
#s_FiltroEsteril := FALSE;
END_IF;
END_REGION TIEMPO_ESTERILIZADO
// ==========================================================================================================
REGION TIEMPO_ETAPA
// Proceso en estado parado
IF #s_Etapa = #REPOSO THEN
#s_TiempoEtapa.Actual := 0;
#s_TiempoEtapa.Activar := FALSE;
END_IF;
// Temporizador activo
IF #s_TiempoEtapa.Activar THEN
IF #Pulso1Seg
AND
(#s_TiempoEtapa.Actual < #s_TiempoEtapa.SP)
THEN
#s_TiempoEtapa.Actual += 1;
END_IF;
// Reiniciar tiempo paso cumplido para esperar condicion
#s_TiempoEtapa.Cumplido := FALSE;
// Tiempo paso cumplido
IF #s_TiempoEtapa.Actual >= #s_TiempoEtapa.SP AND #s_TiempoEtapa.Activar THEN
#s_TiempoEtapa.Cumplido := TRUE;
END_IF;
// Tiempo restante
#s_TiempoEtapa.Restante := #s_TiempoEtapa.SP - #s_TiempoEtapa.Actual;
END_IF;
// Tiempo restante
#s_TiempoEtapa.Restante := #s_TiempoEtapa.SP - #s_TiempoEtapa.Actual;
END_REGION TIEMPO_ETAPA
// ==========================================================================================================
REGION MANTENIMIENTO
// Flanco mostrar cartel de mantenieminto
IF #HabilitarMantenimiento THEN
#s_MostrarAlarmaMto := (#FechaHora.MINUTE = #MinutoAlarma) AND (#FechaHora.SECOND = #SegundoAlarma) AND (#s_NumeroEsterilizaciones >= #SP_NumeroMaximoEsterilizacion);
IF #s_MostrarAlarmaMto AND NOT #s_oldMostrarAlarmaMto THEN
#ParoMantenimiento := TRUE;
#s_PasswordCalculado :=
#s_PasswordHMI := 0;
END_IF;
// Pulsador chequear contraseña de mantenimiento
IF #Pulsadores.Mantenimiento.Plc THEN
#s_PasswordCalculado := #FechaHora.YEAR + #FechaHora.MONTH + #FechaHora.DAY;
IF #s_PasswordCalculado = #s_PasswordHMI THEN
#s_NumeroEsterilizaciones := 0;
#S_NumeroDiasEsterilizacion := 0;
END_IF;
#s_PasswordCalculado :=
#s_PasswordHMI := 0;
#ParoMantenimiento := FALSE;
END_IF;
ELSE
#ParoMantenimiento := FALSE;
END_IF;
#s_oldMostrarAlarmaMto := #s_MostrarAlarmaMto;
END_REGION MANTENIMIENTO
// ==========================================================================================================
REGION ALARMAS
// Aviso un dia antes
#t_DiasAvisos := #SP_DiasMaximoFiltroEsteril - 1;
IF #t_DiasAvisos < 0 THEN
#t_DiasAvisos := 0;
END_IF;
// Alarmas por dia de esterilizacion
#AlarmaDiasEsterilizacion := (#S_NumeroDiasEsterilizacion >= #SP_DiasMaximoFiltroEsteril);
#AvisoDiasEsterilizacion := (#S_NumeroDiasEsterilizacion >= #t_DiasAvisos AND NOT #AlarmaDiasEsterilizacion);
END_REGION ALARMAS
// ==========================================================================================================
REGION NUMERO_ESTERILIZACIONES
IF #s_NumeroEsterilizaciones = 0 THEN
#s_NumeroEsterilizaciones += 1;
END_IF;
END_REGION NUMERO_ESTERILIZACIONES
END_FUNCTION_BLOCK
@@ -0,0 +1,652 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_mA";
S7_BlockNumber := "2017";
S7_BlockTitle := "MLC_58v";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION_BLOCK "FB2017_ZC_M_SINA"
VAR_INPUT
{
S7_Access := "ReadOnly := External";
S7_MLC := "MLC_3jh";
S7_Visibility := "Hidden := External"
}
Pulso1seg : Bool;
{
S7_Access := "ReadOnly := External";
S7_MLC := "MLC_33h";
S7_Visibility := "Hidden := External"
}
Simulacion : Bool;
{
S7_Access := "ReadOnly := External";
S7_MLC := "MLC_4tV";
S7_Visibility := "Hidden := External"
}
Arranque : Bool;
{
S7_Access := "ReadOnly := External";
S7_MLC := "MLC_4Yr";
S7_Visibility := "Hidden := External"
}
Ack : Bool;
{
S7_Access := "ReadOnly := External";
S7_MLC := "MLC_3v9";
S7_Visibility := "Hidden := External"
}
SeguridadesOK : Bool;
{
S7_Access := "ReadOnly := External";
S7_MLC := "MLC_3LS";
S7_Visibility := "Hidden := External"
}
UsuarioActual : String;
{
S7_Access := "ReadOnly := External";
S7_MLC := "MLC_3Vs";
S7_Visibility := "Hidden := External"
}
FechaHoraActual : DTL;
END_VAR
VAR
{ S7_MLC := "MLC_4vz" }
HWIDSTW : Array[0.._.N_MAX_DISP_M_SINA] of HW_IO;
{ S7_MLC := "MLC_53F" }
HWIDZSW : Array[0.._.N_MAX_DISP_M_SINA] of HW_IO;
SinaSpeed : Array[0.._.N_MAX_DISP_M_SINA] of SinaSpeed;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_Frecuencia : Real;
t_ErrorSinaSpeed : Bool;
t_ErrorTermico : Bool;
t_ConfigAxis : Word;
t_ZSW1 : Bool;
END_VAR
VAR CONSTANT
SOLO_DIRECTO : USInt := 0;
DIRECTO_INVERSO : USInt := 1;
END_VAR
{ S7_Language := "SCL" }
NETWORK
(*
ZEUS CONTROL, S.A.
(c)Copyright (2025) All Rights Reserved
--------------------------------------------------------------------------------------
Software: TIA Portal 18
Restricciones: PLC serie 1200/1500
Nombre: DISPOSITIVO SINAMICS
Descripcion: Modelo de dispositivo sinamics (comunicado)
Dependencias:
FC: FC8_ZC_TRAZA_REGISTRO
FB: -
DB: DB2017_DISP_M_SINA
DB2018_INST_M_SINA
DB1_SYS
UDT: ZC_DISP_M_SINA
ZC_DISP_MUX_M_SINA
Change log:
Version Fecha Tecnico a cargo Descripcion
01.00.00 27.04.2020 (ABH) Primera version.
01.00.01 26.05.2020 (ABH) Se añade logica como dispositivo motor VF.
01.00.02 22.09.2022 (HCR) Cambio lógica SinaSpeed
Se añade trazabilidad manualizaciones
Se añade trazabilidad de activación manual
01.00.03 08.11.2022 (HCR) Cambio lógica enclavamiento
01.00.04 18.09.2023 (ABH) Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO
01.00.05 28.02.2024 (ABH) Se añade gestion multiplexado para Scada (no indexado)
01.00.06 26.03.2025 (ABH) Se añade variable grupo de alarma para separar los nuevos avisos por cuadro.
01.00.07 07.10.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.
02.00.00 17.12.2025 (ABH) Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se añade indexHMI para lista de
texto en sistemas de supervision.
//=====================================================================================
*)
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 Motor con variador de frecuencia SINAMICS.
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 tiempo en marcha
- Se gestiona la orden de escritura
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO`, `FC15025_ZC_CONTADOR_TIEMPO` |
| FB | - |
| DB | `DB2017_DISP_M_SINA` |
| UDT | `ZC_DISP_M_SINA`, `ZC_DISP_MUX_M_SINA`, `ZC_CONTADOR_TIEMPO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 27.04.2020 | (ABH) | Primera version. |
| 00.00.02 | 26.05.2020 | (ABH) | Se añade logica como dispositivo motor VF. |
| 00.00.03 | 22.09.2022 | (ABH) | Cambio lógica SinaSpeed, Se añade trazabilidad manualizaciones, Se añade trazabilidad de activación manual |
| 00.00.04 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento. |
| 00.00.05 | 18.09.2023 | (ABH) | Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO. |
| 00.00.06 | 28.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado). |
| 00.00.07 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.08 | 07.10.2025 | (HCR) | 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. |
| 00.00.09 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento |
| 00.00.10 | 18.09.2023 | (ABH) | Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO |
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado Se añade movimiento de M[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.13 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 00.00.14 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.00.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2017_M_SINA".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2017_M_SINA".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2017_M_SINA".NumDispositivos := "N_MAX_DISP_M_SINA";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_M_SINA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_M_SINA";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
REGION LIMITE_GRUPO_ALARMA
IF "DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma < 0 THEN
"DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2017_M_SINA".M_SINA[#for_i].Config_Habilitar THEN
// =============================================================================
// PUESTA A CERO DE LA SALIDA
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
// =============================================================================
// INICIALIZACION DE VALORES QUE SIEMPRE TIENEN QUE ESTAR A 1 EN LA PALABRA DE MANDO
#t_ConfigAxis := 16#003F;
REGION LECTURA_VALORES_DE_LA_TARJETA
// =============================================================================
// RETORNO TERMICO
IF "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoByte >= 0 THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_RetornoTermico := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoBit);
IF "DB2017_M_SINA".M_SINA[#for_i].Config_HabilitarInvertirTermico THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_RetornoTermico := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoBit));
END_IF;
END_IF;
END_REGION
REGION MODOS_DE_TRABAJO
// =============================================================================
// RESET CON LOCKOUT
IF #SinaSpeed[#for_i].Lockout AND NOT #Simulacion THEN
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarAuto := FALSE;
END_IF;
// =============================================================================
// MODO MANUAL
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan THEN
"DB2017_M_SINA".Agrup["DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual AND NOT "DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2017_M_SINA".M_SINA[#for_i].Estado_SeguridadOk AND NOT "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico THEN
#t_Activar := TRUE;
#t_Frecuencia := "DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaManual;
ELSE
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := "DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaAuto;
IF "DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarAuto AND NOT "DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2017_M_SINA".M_SINA[#for_i].Estado_SeguridadOk AND NOT "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico THEN
#t_Activar := TRUE;
#t_Frecuencia := "DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaAuto;
ELSE
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
END_IF;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaManual := "DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaAuto;
END_IF;
END_REGION
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// MOTOR SIMULADO Y NO SIMULADO
IF #Simulacion THEN
IF #t_Activar THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := TRUE;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := FALSE;
END_IF;
"DB2017_M_SINA".M_SINA[#for_i].Estado_ValorActual := #t_Frecuencia;
ELSE
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_EjeHabilitado THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := TRUE;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := FALSE;
END_IF;
IF #SinaSpeed[#for_i].ActVelocity > 0.0 THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_ValorActual := #SinaSpeed[#for_i].ActVelocity * "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMax / "DB2017_M_SINA".M_SINA[#for_i].Config_K_Velocidad;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Estado_ValorActual := #SinaSpeed[#for_i].ActVelocity;
END_IF;
END_IF;
END_REGION
REGION ERRORES
// =============================================================================
// CHEQUEO RETORNO TERMICO
IF NOT #Simulacion AND "DB2017_M_SINA".M_SINA[#for_i].Config_HabilitarRetornoTermico
AND "DB2017_M_SINA".M_SINA[#for_i].Estado_RetornoTermico THEN
#t_ErrorTermico := TRUE;
ELSE
#t_ErrorTermico := FALSE;
END_IF;
// =============================================================================
// CHEQUEO ERROR DESDE EL BLOQUE SINA SPEED
IF NOT #Simulacion AND #SinaSpeed[#for_i].Error AND (#SinaSpeed[#for_i].Status <> 16#8402) THEN
#t_ErrorSinaSpeed := TRUE;
ELSE
#t_ErrorSinaSpeed := FALSE;
END_IF;
// =============================================================================
// TEMPORIZADO DE ERRORES
IF #t_ErrorSinaSpeed
AND NOT "DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed
AND NOT "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico
THEN
IF NOT #Simulacion AND #Pulso1seg THEN
"DB2017_M_SINA".M_SINA[#for_i].Tiempos_Alarma += 1;
END_IF;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Tiempos_Alarma := 0;
END_IF;
// =============================================================================
// ERROR TERMICO
IF #t_ErrorTermico THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico := TRUE;
END_IF;
IF NOT #t_ErrorTermico THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico := FALSE;
END_IF;
// =============================================================================
// ERROR DESDE EL BLOQUE SINA SPEED
IF "DB2017_M_SINA".M_SINA[#for_i].Tiempos_Alarma >= "DB2017_M_SINA".M_SINA[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorSinaSpeed THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed := TRUE;
END_IF;
IF NOT #t_ErrorSinaSpeed THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed := FALSE;
END_IF;
// =============================================================================
// ERROR GLOBAL
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_General := "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico OR "DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed;
// =============================================================================
// SI TENEMOS UN ERROR DESACTIVAMOS LA SALIDA
IF "DB2017_M_SINA".M_SINA[#for_i].Alarmas_General THEN
"DB2017_M_SINA".Agrup["DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
#t_Activar := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
END_IF;
END_REGION
REGION DIAGNOSIS
// =============================================================================
// CONTADOR DE HORAS DE FUNCIONAMIENTO
IF "DB2017_M_SINA".M_SINA[#for_i].Orden_ResetHoras THEN
"DB2017_M_SINA".M_SINA[#for_i].Orden_ResetHoras := FALSE;
END_IF;
IF NOT #Simulacion AND #Pulso1seg AND "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado THEN
"DB2017_M_SINA".M_SINA[#for_i].Tiempos_Mantenimiento += 0.00027778;
END_IF;
// =============================================================================
// ALARMA HORAS PARA MANTENIMIENTO ALCANZADOS
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Mantenimiento := FALSE;
IF "DB2017_M_SINA".M_SINA[#for_i].Config_HabilitarMantenimiento
AND
"DB2017_M_SINA".M_SINA[#for_i].Tiempos_Mantenimiento > "DB2017_M_SINA".M_SINA[#for_i].Tiempos_SetPointMantenimiento
THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Mantenimiento := TRUE;
END_IF;
END_REGION
REGION ESCRITURA_SALIDA
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := #t_Frecuencia;
// =============================================================================
// LIMITAR SALIDA A LOS VALORES ESTABLECIDOS EN EL ELEMENTO
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida > "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMax THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMax;
END_IF;
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida < "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMin THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMin;
END_IF;
(*
// =============================================================================
// PERMISO PARA INVERTIR EL SENTIDO DE GIRO
"DB52_DISP_M_SINA".SINA[#i].Estado_PermisoInvertirSentido := NOT #tempActivar;
// =============================================================================
// REINICIALIZO LA PALABRA DE SALIDAS EN CASO DE QUE HAYAMOS INVERTIDO EL SENTIDO DE GIRO
IF "DB52_DISP_M_SINA".SINA[#i].Orden_SeleccionInverso THEN
#tempConfigAxis := 16#00FE;
END_IF;
*)
// =============================================================================
// ESCRITURA SALIDA SI NO ESTA LA ORDEN DE ACTIVAR
IF NOT #t_Activar THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := 0.0;
END_IF;
// =============================================================================
// CONVERSION DE Hz A VALOR EN RPM
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida > 0.0 THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := ("DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida * "DB2017_M_SINA".M_SINA[#for_i].Config_K_Velocidad) / "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMax;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := 0.0;
END_IF;
END_REGION
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarAuto := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado := FALSE;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan AND NOT "DB2017_M_SINA".M_SINA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_17_DISP_M_SINA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_SINA",
CodInt_2 := #for_i,
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 "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan AND "DB2017_M_SINA".M_SINA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_17_DISP_M_SINA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_SINA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan AND "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado AND NOT "DB2017_M_SINA".M_SINA[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_17_DISP_M_SINA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_SINA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan AND NOT "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado AND "DB2017_M_SINA".M_SINA[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_17_DISP_M_SINA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_SINA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2017_M_SINA".M_SINA[#for_i].Aux_oldAutoMan := "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan;
"DB2017_M_SINA".M_SINA[#for_i].Aux_oldActivado := "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_General := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Mantenimiento := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_RetornoTermico := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarAuto := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_ValorActual := 0.0;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X0 := "DB2017_M_SINA".M_SINA[#for_i].Config_Habilitar;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X1 := "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X2 := "DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X3 := "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X8 :="DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X9 := "DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X13 := "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Mantenimiento;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2017_M_SINA".M_SINA[#for_i].Alarmas_General AND NOT "DB2017_M_SINA".M_SINA[#for_i].Aux_oldAlarma THEN
"DB2017_M_SINA".Agrup["DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2017_M_SINA".M_SINA[#for_i].Aux_oldAlarma := "DB2017_M_SINA".M_SINA[#for_i].Alarmas_General;
END_REGION
REGION SINASPEED
IF #HWIDSTW[#for_i] <> 0 AND #HWIDZSW[#for_i] <> 0 THEN
#SinaSpeed[#for_i](EnableAxis := #t_Activar,
AckError := "DB2017_M_SINA".M_SINA[#for_i].Orden_ResetFallos OR #Ack,
SpeedSp := "DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida,
RefSpeed := "DB2017_M_SINA".M_SINA[#for_i].Config_K_Velocidad,
ConfigAxis := #t_ConfigAxis,
HWIDSTW := #HWIDSTW[#for_i],
HWIDZSW := #HWIDZSW[#for_i],
AxisEnabled => "DB2017_M_SINA".M_SINA[#for_i].Estado_EjeHabilitado,
Lockout => "DB2017_M_SINA".M_SINA[#for_i].Estado_EjeBloqueado,
Status => "DB2017_M_SINA".M_SINA[#for_i].Estado_Estado,
DiagId => "DB2017_M_SINA".M_SINA[#for_i].Estado_CodigoFallo);
END_IF;
// =============================================================================
// ESCRITURA RESET FALLOS
IF "DB2017_M_SINA".M_SINA[#for_i].Orden_ResetFallos AND #Pulso1seg THEN // Reset de este BIT cada segundo (es suficiente para resetear el Variador)
"DB2017_M_SINA".M_SINA[#for_i].Orden_ResetFallos := FALSE;
END_IF;
END_REGION
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2017_M_SINA".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION
END_NETWORK
END_FUNCTION_BLOCK
@@ -0,0 +1,23 @@
MultiLingualTexts:
- id: MLC_58v
es-ES: FB2017_M_SINA
- id: MLC_mA
es-ES: Bloque de funcion para gestion de dispositivo motor sinamics
- id: MLC_3jh
es-ES: Pulso de 1 segundo
- id: MLC_33h
es-ES: Simulacion
- id: MLC_4tV
es-ES: Primer arranque
- id: MLC_4Yr
es-ES: Acuse general
- id: MLC_3v9
es-ES: Seguridades OK
- id: MLC_3LS
es-ES: Usuario actual
- id: MLC_3Vs
es-ES: Fecha y hora actual
- id: MLC_4vz
es-ES: Hardware Identifer set point slot
- id: MLC_53F
es-ES: Hardware Identifer actual value slot
@@ -1,614 +0,0 @@
FUNCTION_BLOCK "FB2017_ZC_M_SINA"
TITLE = FB2017_M_SINA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Bloque de funcion para gestion de dispositivo motor sinamics
VAR_INPUT
Pulso1seg { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Pulso de 1 segundo
Simulacion { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Simulacion
Arranque { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Primer arranque
Ack { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Acuse general
SeguridadesOK { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Seguridades OK
UsuarioActual { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DTL; // Fecha y hora actual
END_VAR
VAR
HWIDSTW : Array[0.."N_MAX_DISP_M_SINA"] of HW_IO; // Hardware Identifer set point slot
HWIDZSW : Array[0.."N_MAX_DISP_M_SINA"] of HW_IO; // Hardware Identifer actual value slot
SinaSpeed {InstructionName := 'SinaSpeed'; LibVersion := '1.0'} : Array[0.."N_MAX_DISP_M_SINA"] of SinaSpeed;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_Frecuencia : Real;
t_ErrorSinaSpeed : Bool;
t_ErrorTermico : Bool;
t_ConfigAxis : Word;
t_ZSW1 : Bool;
END_VAR
VAR CONSTANT
SOLO_DIRECTO : USInt := 0;
DIRECTO_INVERSO : USInt := 1;
END_VAR
BEGIN
(*
ZEUS CONTROL, S.A.
(c)Copyright (2025) All Rights Reserved
--------------------------------------------------------------------------------------
Software: TIA Portal 18
Restricciones: PLC serie 1200/1500
Nombre: DISPOSITIVO SINAMICS
Descripcion: Modelo de dispositivo sinamics (comunicado)
Dependencias:
FC: FC8_ZC_TRAZA_REGISTRO
FB: -
DB: DB2017_DISP_M_SINA
DB2018_INST_M_SINA
DB1_SYS
UDT: ZC_DISP_M_SINA
ZC_DISP_MUX_M_SINA
Change log:
Version Fecha Tecnico a cargo Descripcion
01.00.00 27.04.2020 (ABH) Primera version.
01.00.01 26.05.2020 (ABH) Se añade logica como dispositivo motor VF.
01.00.02 22.09.2022 (HCR) Cambio lógica SinaSpeed
Se añade trazabilidad manualizaciones
Se añade trazabilidad de activación manual
01.00.03 08.11.2022 (HCR) Cambio lógica enclavamiento
01.00.04 18.09.2023 (ABH) Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO
01.00.05 28.02.2024 (ABH) Se añade gestion multiplexado para Scada (no indexado)
01.00.06 26.03.2025 (ABH) Se añade variable grupo de alarma para separar los nuevos avisos por cuadro.
01.00.07 07.10.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.
02.00.00 17.12.2025 (ABH) Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se añade indexHMI para lista de
texto en sistemas de supervision.
//=====================================================================================
*)
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 Motor con variador de frecuencia SINAMICS.
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 tiempo en marcha
- Se gestiona la orden de escritura
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO`, `FC15025_ZC_CONTADOR_TIEMPO` |
| FB | - |
| DB | `DB2017_DISP_M_SINA` |
| UDT | `ZC_DISP_M_SINA`, `ZC_DISP_MUX_M_SINA`, `ZC_CONTADOR_TIEMPO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 27.04.2020 | (ABH) | Primera version. |
| 00.00.02 | 26.05.2020 | (ABH) | Se añade logica como dispositivo motor VF. |
| 00.00.03 | 22.09.2022 | (ABH) | Cambio lógica SinaSpeed, Se añade trazabilidad manualizaciones, Se añade trazabilidad de activación manual |
| 00.00.04 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento. |
| 00.00.05 | 18.09.2023 | (ABH) | Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO. |
| 00.00.06 | 28.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado). |
| 00.00.07 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.08 | 07.10.2025 | (HCR) | 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. |
| 00.00.09 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento |
| 00.00.10 | 18.09.2023 | (ABH) | Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO |
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado Se añade movimiento de M[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.13 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 00.00.14 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.00.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2017_M_SINA".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2017_M_SINA".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2017_M_SINA".NumDispositivos := "N_MAX_DISP_M_SINA";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_M_SINA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_M_SINA";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
REGION LIMITE_GRUPO_ALARMA
IF "DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma < 0 THEN
"DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2017_M_SINA".M_SINA[#for_i].Config_Habilitar THEN
// =============================================================================
// PUESTA A CERO DE LA SALIDA
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
// =============================================================================
// INICIALIZACION DE VALORES QUE SIEMPRE TIENEN QUE ESTAR A 1 EN LA PALABRA DE MANDO
#t_ConfigAxis := 16#003F;
REGION LECTURA_VALORES_DE_LA_TARJETA
// =============================================================================
// RETORNO TERMICO
IF "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoByte >= 0 THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_RetornoTermico := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoBit);
IF "DB2017_M_SINA".M_SINA[#for_i].Config_HabilitarInvertirTermico THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_RetornoTermico := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2017_M_SINA".M_SINA[#for_i].Config_DireccionRetTermicoBit));
END_IF;
END_IF;
END_REGION
REGION MODOS_DE_TRABAJO
// =============================================================================
// RESET CON LOCKOUT
IF #SinaSpeed[#for_i].Lockout AND NOT #Simulacion THEN
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarAuto := FALSE;
END_IF;
// =============================================================================
// MODO MANUAL
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan THEN
"DB2017_M_SINA".Agrup["DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual AND NOT "DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2017_M_SINA".M_SINA[#for_i].Estado_SeguridadOk AND NOT "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico THEN
#t_Activar := TRUE;
#t_Frecuencia := "DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaManual;
ELSE
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := "DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaAuto;
IF "DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarAuto AND NOT "DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2017_M_SINA".M_SINA[#for_i].Estado_SeguridadOk AND NOT "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico THEN
#t_Activar := TRUE;
#t_Frecuencia := "DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaAuto;
ELSE
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
END_IF;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaManual := "DB2017_M_SINA".M_SINA[#for_i].Orden_ConsignaAuto;
END_IF;
END_REGION
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// MOTOR SIMULADO Y NO SIMULADO
IF #Simulacion THEN
IF #t_Activar THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := TRUE;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := FALSE;
END_IF;
"DB2017_M_SINA".M_SINA[#for_i].Estado_ValorActual := #t_Frecuencia;
ELSE
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_EjeHabilitado THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := TRUE;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := FALSE;
END_IF;
IF #SinaSpeed[#for_i].ActVelocity > 0.0 THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_ValorActual := #SinaSpeed[#for_i].ActVelocity * "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMax / "DB2017_M_SINA".M_SINA[#for_i].Config_K_Velocidad;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Estado_ValorActual := #SinaSpeed[#for_i].ActVelocity;
END_IF;
END_IF;
END_REGION
REGION ERRORES
// =============================================================================
// CHEQUEO RETORNO TERMICO
IF NOT #Simulacion AND "DB2017_M_SINA".M_SINA[#for_i].Config_HabilitarRetornoTermico
AND "DB2017_M_SINA".M_SINA[#for_i].Estado_RetornoTermico THEN
#t_ErrorTermico := TRUE;
ELSE
#t_ErrorTermico := FALSE;
END_IF;
// =============================================================================
// CHEQUEO ERROR DESDE EL BLOQUE SINA SPEED
IF NOT #Simulacion AND #SinaSpeed[#for_i].Error AND (#SinaSpeed[#for_i].Status <> 16#8402) THEN
#t_ErrorSinaSpeed := TRUE;
ELSE
#t_ErrorSinaSpeed := FALSE;
END_IF;
// =============================================================================
// TEMPORIZADO DE ERRORES
IF #t_ErrorSinaSpeed
AND NOT "DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed
AND NOT "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico
THEN
IF NOT #Simulacion AND #Pulso1seg THEN
"DB2017_M_SINA".M_SINA[#for_i].Tiempos_Alarma += 1;
END_IF;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Tiempos_Alarma := 0;
END_IF;
// =============================================================================
// ERROR TERMICO
IF #t_ErrorTermico THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico := TRUE;
END_IF;
IF NOT #t_ErrorTermico THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico := FALSE;
END_IF;
// =============================================================================
// ERROR DESDE EL BLOQUE SINA SPEED
IF "DB2017_M_SINA".M_SINA[#for_i].Tiempos_Alarma >= "DB2017_M_SINA".M_SINA[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorSinaSpeed THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed := TRUE;
END_IF;
IF NOT #t_ErrorSinaSpeed THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed := FALSE;
END_IF;
// =============================================================================
// ERROR GLOBAL
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_General := "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico OR "DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed;
// =============================================================================
// SI TENEMOS UN ERROR DESACTIVAMOS LA SALIDA
IF "DB2017_M_SINA".M_SINA[#for_i].Alarmas_General THEN
"DB2017_M_SINA".Agrup["DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
#t_Activar := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
END_IF;
END_REGION
REGION DIAGNOSIS
// =============================================================================
// CONTADOR DE HORAS DE FUNCIONAMIENTO
IF "DB2017_M_SINA".M_SINA[#for_i].Orden_ResetHoras THEN
"DB2017_M_SINA".M_SINA[#for_i].Orden_ResetHoras := FALSE;
END_IF;
IF NOT #Simulacion AND #Pulso1seg AND "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado THEN
"DB2017_M_SINA".M_SINA[#for_i].Tiempos_Mantenimiento += 0.00027778;
END_IF;
// =============================================================================
// ALARMA HORAS PARA MANTENIMIENTO ALCANZADOS
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Mantenimiento := FALSE;
IF "DB2017_M_SINA".M_SINA[#for_i].Config_HabilitarMantenimiento
AND
"DB2017_M_SINA".M_SINA[#for_i].Tiempos_Mantenimiento > "DB2017_M_SINA".M_SINA[#for_i].Tiempos_SetPointMantenimiento
THEN
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Mantenimiento := TRUE;
END_IF;
END_REGION
REGION ESCRITURA_SALIDA
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := #t_Frecuencia;
// =============================================================================
// LIMITAR SALIDA A LOS VALORES ESTABLECIDOS EN EL ELEMENTO
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida > "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMax THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMax;
END_IF;
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida < "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMin THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMin;
END_IF;
(*
// =============================================================================
// PERMISO PARA INVERTIR EL SENTIDO DE GIRO
"DB52_DISP_M_SINA".SINA[#i].Estado_PermisoInvertirSentido := NOT #tempActivar;
// =============================================================================
// REINICIALIZO LA PALABRA DE SALIDAS EN CASO DE QUE HAYAMOS INVERTIDO EL SENTIDO DE GIRO
IF "DB52_DISP_M_SINA".SINA[#i].Orden_SeleccionInverso THEN
#tempConfigAxis := 16#00FE;
END_IF;
*)
// =============================================================================
// ESCRITURA SALIDA SI NO ESTA LA ORDEN DE ACTIVAR
IF NOT #t_Activar THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := 0.0;
END_IF;
// =============================================================================
// CONVERSION DE Hz A VALOR EN RPM
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida > 0.0 THEN
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := ("DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida * "DB2017_M_SINA".M_SINA[#for_i].Config_K_Velocidad) / "DB2017_M_SINA".M_SINA[#for_i].Config_VelocidadMax;
ELSE
"DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida := 0.0;
END_IF;
END_REGION
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarAuto := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado := FALSE;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan AND NOT "DB2017_M_SINA".M_SINA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_17_DISP_M_SINA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_SINA",
CodInt_2 := #for_i,
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 "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan AND "DB2017_M_SINA".M_SINA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_17_DISP_M_SINA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_SINA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan AND "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado AND NOT "DB2017_M_SINA".M_SINA[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_17_DISP_M_SINA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_SINA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan AND NOT "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado AND "DB2017_M_SINA".M_SINA[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_17_DISP_M_SINA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_SINA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2017_M_SINA".M_SINA[#for_i].Aux_oldAutoMan := "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan;
"DB2017_M_SINA".M_SINA[#for_i].Aux_oldActivado := "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_General := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_Mantenimiento := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_RetornoTermico := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarAuto := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Orden_ActivarManual := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_Activado := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Estado_ValorActual := 0.0;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X0 := "DB2017_M_SINA".M_SINA[#for_i].Config_Habilitar;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X1 := "DB2017_M_SINA".M_SINA[#for_i].Estado_AutoMan;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X2 := "DB2017_M_SINA".M_SINA[#for_i].Estado_Enclavado;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X3 := "DB2017_M_SINA".M_SINA[#for_i].Estado_Activado;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X8 :="DB2017_M_SINA".M_SINA[#for_i].Alarmas_Termico;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X9 := "DB2017_M_SINA".M_SINA[#for_i].Alarmas_SinaSpeed;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X13 := "DB2017_M_SINA".M_SINA[#for_i].Alarmas_Mantenimiento;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2017_M_SINA".M_SINA[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2017_M_SINA".M_SINA[#for_i].Alarmas_General AND NOT "DB2017_M_SINA".M_SINA[#for_i].Aux_oldAlarma THEN
"DB2017_M_SINA".Agrup["DB2017_M_SINA".M_SINA[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2017_M_SINA".M_SINA[#for_i].Aux_oldAlarma := "DB2017_M_SINA".M_SINA[#for_i].Alarmas_General;
END_REGION
REGION SINASPEED
IF #HWIDSTW[#for_i] <> 0 AND #HWIDZSW[#for_i] <> 0 THEN
#SinaSpeed[#for_i](EnableAxis := #t_Activar,
AckError := "DB2017_M_SINA".M_SINA[#for_i].Orden_ResetFallos OR #Ack,
SpeedSp := "DB2017_M_SINA".M_SINA[#for_i].Estado_VelocidadSalida,
RefSpeed := "DB2017_M_SINA".M_SINA[#for_i].Config_K_Velocidad,
ConfigAxis := #t_ConfigAxis,
HWIDSTW := #HWIDSTW[#for_i],
HWIDZSW := #HWIDZSW[#for_i],
AxisEnabled => "DB2017_M_SINA".M_SINA[#for_i].Estado_EjeHabilitado,
Lockout => "DB2017_M_SINA".M_SINA[#for_i].Estado_EjeBloqueado,
Status => "DB2017_M_SINA".M_SINA[#for_i].Estado_Estado,
DiagId => "DB2017_M_SINA".M_SINA[#for_i].Estado_CodigoFallo);
END_IF;
// =============================================================================
// ESCRITURA RESET FALLOS
IF "DB2017_M_SINA".M_SINA[#for_i].Orden_ResetFallos AND #Pulso1seg THEN // Reset de este BIT cada segundo (es suficiente para resetear el Variador)
"DB2017_M_SINA".M_SINA[#for_i].Orden_ResetFallos := FALSE;
END_IF;
END_REGION
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2017_M_SINA".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION
END_FUNCTION_BLOCK
@@ -0,0 +1,377 @@
{
S7_Author := "HCR";
S7_BlockComment := "MLC_59U";
S7_BlockNumber := "2000";
S7_BlockTitle := "MLC_49Z";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2000_ZC_ED" : Void
VAR_INPUT
{ S7_MLC := "MLC_4ZE" }
Pulso1seg : Bool;
{ S7_MLC := "MLC_4py" }
Simulacion : Bool;
{ S7_MLC := "MLC_4LR" }
UsuarioActual : String;
{ S7_MLC := "MLC_3kM" }
FechaHoraActual : DTL;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_ValorEntrada : Bool;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Entrada Digital.
En ella se realizan las siguientes acciones:
- Se gestiona el multiplexado de los dispositivos para su uso en sistemas HMI/SCADA.
- Se realiza la lectura de la entrada de manera indirecta si esta habilitada.
- Se gestiona el estado del dispositivo
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | `DB2000_ED` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2020 | (ABH) | Primera version. |
| 00.01.00 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. |
| 00.02.00 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.03 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.04 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado. Se añade movimiento de SD[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.05 | 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. |
| 00.00.06 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.07 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.08 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.09 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 00.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.00.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2000_ED".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2000_ED".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2000_ED".NumDispositivos := "N_MAX_DISP_ED";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_ED" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_ED";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2000_ED".ED[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2000_ED".ED[#for_i].Config_GrupoAlarma < 0 THEN
"DB2000_ED".ED[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2000_ED".ED[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2000_ED".ED[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
REGION LECTURA_VALORES
CASE "DB2000_ED".ED[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
IF "DB2000_ED".ED[#for_i].Config_DirEntradaByte >= 0 THEN
(*
AREA: Pueden seleccionarse las siguientes áreas:
16#81: Input,
16#82: Output,
16#83: Marcas,
16#84: DB,
16#1: Entrada de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección en la que se lee. Solo se utilizan los 16 bits menos significativos.*)
#t_ValorEntrada := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2000_ED".ED[#for_i].Config_DirEntradaByte,
bitOffset := "DB2000_ED".ED[#for_i].Config_DirEntradaBit);
IF NOT "DB2000_ED".ED[#for_i].Config_HabilitarInvertir THEN
"DB2000_ED".ED[#for_i].Estado_EntradaCanal := NOT #t_ValorEntrada;
ELSE
"DB2000_ED".ED[#for_i].Estado_EntradaCanal := #t_ValorEntrada;
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
;
END_CASE;
END_REGION LECTURA_VALORES
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2000_ED".ED[#for_i].Estado_AutoMan THEN
"DB2000_ED".Agrup["DB2000_ED".ED[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2000_ED".ED[#for_i].Orden_ActivarManual THEN
"DB2000_ED".ED[#for_i].Estado_Activado := TRUE;
ELSE
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
"DB2000_ED".ED[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2000_ED".ED[#for_i].Orden_ActivarManual := "DB2000_ED".ED[#for_i].Estado_Activado;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// DETECTOR SIMULADO
IF #Simulacion THEN
IF "DB2000_ED".ED[#for_i].Orden_ActivarManual THEN
"DB2000_ED".ED[#for_i].Estado_Activado := TRUE;
ELSE
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
IF NOT "DB2000_ED".ED[#for_i].Estado_AutoMan AND NOT #Simulacion THEN
// =============================================================================
// RETARDO A LA ACTIVACIÓN AUTOMÁTICO
IF "DB2000_ED".ED[#for_i].Estado_EntradaCanal AND "DB2000_ED".ED[#for_i].Config_HabilitarRetardoAct THEN
IF #Pulso1seg AND "DB2000_ED".ED[#for_i].Tiempos_TiempoAct < "DB2000_ED".ED[#for_i].Tiempos_SPTiempoAct THEN
"DB2000_ED".ED[#for_i].Tiempos_TiempoAct += 1;
END_IF;
IF "DB2000_ED".ED[#for_i].Tiempos_TiempoAct >= "DB2000_ED".ED[#for_i].Tiempos_SPTiempoAct THEN
"DB2000_ED".ED[#for_i].Estado_Activado := TRUE;
END_IF;
ELSIF "DB2000_ED".ED[#for_i].Estado_EntradaCanal THEN
"DB2000_ED".ED[#for_i].Estado_Activado := TRUE;
ELSE
"DB2000_ED".ED[#for_i].Tiempos_TiempoAct := 0;
END_IF;
// =============================================================================
// RETARDO A LA DESACTIVACIÓN AUTOMÁTICO
IF NOT "DB2000_ED".ED[#for_i].Estado_EntradaCanal AND "DB2000_ED".ED[#for_i].Config_HabilitarRetardoDes THEN
IF #Pulso1seg AND "DB2000_ED".ED[#for_i].Tiempos_TiempoDes < "DB2000_ED".ED[#for_i].Tiempos_SPTiempoDes THEN
"DB2000_ED".ED[#for_i].Tiempos_TiempoDes += 1;
END_IF;
IF "DB2000_ED".ED[#for_i].Tiempos_TiempoDes >= "DB2000_ED".ED[#for_i].Tiempos_SPTiempoDes THEN
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
END_IF;
ELSIF NOT "DB2000_ED".ED[#for_i].Estado_EntradaCanal THEN
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
ELSE
"DB2000_ED".ED[#for_i].Tiempos_TiempoDes := 0;
END_IF;
END_IF;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2000_ED".ED[#for_i].Estado_AutoMan AND NOT "DB2000_ED".ED[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_10_DISP_ED",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_ED",
CodInt_2 := #for_i,
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 "DB2000_ED".ED[#for_i].Estado_AutoMan AND "DB2000_ED".ED[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_10_DISP_ED",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_ED",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2000_ED".ED[#for_i].Estado_AutoMan AND "DB2000_ED".ED[#for_i].Estado_Activado AND NOT "DB2000_ED".ED[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_10_DISP_ED",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_ED",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2000_ED".ED[#for_i].Estado_AutoMan AND NOT "DB2000_ED".ED[#for_i].Estado_Activado AND "DB2000_ED".ED[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_10_DISP_ED",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_ED",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION TRAZABILIDAD_MANUALIZACION
// =============================================================================
// ESTADO ANTERIOR MANUAL/AUTOMÁTICO
"DB2000_ED".ED[#for_i].Aux_oldAutoMan := "DB2000_ED".ED[#for_i].Estado_AutoMan;
"DB2000_ED".ED[#for_i].Aux_oldActivado := "DB2000_ED".ED[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2000_ED".ED[#for_i].Estado_EntradaCanal := FALSE;
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
"DB2000_ED".ED[#for_i].Tiempos_TiempoAct := 0;
"DB2000_ED".ED[#for_i].Tiempos_TiempoDes := 0;
END_IF;
REGION ESTADO_HMI
// =============================================================================
// ESTADO BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2000_ED".ED[#for_i].Hmi_Estado.%X0 := "DB2000_ED".ED[#for_i].Config_Habilitar;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X1 := "DB2000_ED".ED[#for_i].Estado_AutoMan;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X2 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X3 := "DB2000_ED".ED[#for_i].Estado_Activado;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X8 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X13 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION ESTADO_HMI
END_FOR;
END_REGION
END_NETWORK
END_FUNCTION
@@ -0,0 +1,13 @@
MultiLingualTexts:
- id: MLC_49Z
es-ES: FC2000_ED
- id: MLC_59U
es-ES: Modelo de dispositivo Entrada digital
- id: MLC_4ZE
es-ES: Pulso de 1 segundo
- id: MLC_4py
es-ES: Simulacion
- id: MLC_4LR
es-ES: Usuario actual
- id: MLC_3kM
es-ES: Fecha y hora actual
@@ -1,371 +0,0 @@
FUNCTION "FC2000_ZC_ED" : Void
TITLE = FC2000_ED
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : HCR
FAMILY : ZeusControl
VERSION : 1.0
//Modelo de dispositivo Entrada digital
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
Simulacion : Bool; // Simulacion
UsuarioActual : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_ValorEntrada : Bool;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Entrada Digital.
En ella se realizan las siguientes acciones:
- Se gestiona el multiplexado de los dispositivos para su uso en sistemas HMI/SCADA.
- Se realiza la lectura de la entrada de manera indirecta si esta habilitada.
- Se gestiona el estado del dispositivo
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | `DB2000_ED` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2020 | (ABH) | Primera version. |
| 00.01.00 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. |
| 00.02.00 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.03 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.04 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado. Se añade movimiento de SD[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.05 | 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. |
| 00.00.06 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.07 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.08 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.09 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 00.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.00.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2000_ED".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2000_ED".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2000_ED".NumDispositivos := "N_MAX_DISP_ED";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_ED" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_ED";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2000_ED".ED[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2000_ED".ED[#for_i].Config_GrupoAlarma < 0 THEN
"DB2000_ED".ED[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2000_ED".ED[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2000_ED".ED[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
REGION LECTURA_VALORES
CASE "DB2000_ED".ED[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
IF "DB2000_ED".ED[#for_i].Config_DirEntradaByte >= 0 THEN
(*
AREA: Pueden seleccionarse las siguientes áreas:
16#81: Input,
16#82: Output,
16#83: Marcas,
16#84: DB,
16#1: Entrada de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección en la que se lee. Solo se utilizan los 16 bits menos significativos.*)
#t_ValorEntrada := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2000_ED".ED[#for_i].Config_DirEntradaByte,
bitOffset := "DB2000_ED".ED[#for_i].Config_DirEntradaBit);
IF NOT "DB2000_ED".ED[#for_i].Config_HabilitarInvertir THEN
"DB2000_ED".ED[#for_i].Estado_EntradaCanal := NOT #t_ValorEntrada;
ELSE
"DB2000_ED".ED[#for_i].Estado_EntradaCanal := #t_ValorEntrada;
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
;
END_CASE;
END_REGION LECTURA_VALORES
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2000_ED".ED[#for_i].Estado_AutoMan THEN
"DB2000_ED".Agrup["DB2000_ED".ED[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2000_ED".ED[#for_i].Orden_ActivarManual THEN
"DB2000_ED".ED[#for_i].Estado_Activado := TRUE;
ELSE
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
"DB2000_ED".ED[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2000_ED".ED[#for_i].Orden_ActivarManual := "DB2000_ED".ED[#for_i].Estado_Activado;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// DETECTOR SIMULADO
IF #Simulacion THEN
IF "DB2000_ED".ED[#for_i].Orden_ActivarManual THEN
"DB2000_ED".ED[#for_i].Estado_Activado := TRUE;
ELSE
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
IF NOT "DB2000_ED".ED[#for_i].Estado_AutoMan AND NOT #Simulacion THEN
// =============================================================================
// RETARDO A LA ACTIVACIÓN AUTOMÁTICO
IF "DB2000_ED".ED[#for_i].Estado_EntradaCanal AND "DB2000_ED".ED[#for_i].Config_HabilitarRetardoAct THEN
IF #Pulso1seg AND "DB2000_ED".ED[#for_i].Tiempos_TiempoAct < "DB2000_ED".ED[#for_i].Tiempos_SPTiempoAct THEN
"DB2000_ED".ED[#for_i].Tiempos_TiempoAct += 1;
END_IF;
IF "DB2000_ED".ED[#for_i].Tiempos_TiempoAct >= "DB2000_ED".ED[#for_i].Tiempos_SPTiempoAct THEN
"DB2000_ED".ED[#for_i].Estado_Activado := TRUE;
END_IF;
ELSIF "DB2000_ED".ED[#for_i].Estado_EntradaCanal THEN
"DB2000_ED".ED[#for_i].Estado_Activado := TRUE;
ELSE
"DB2000_ED".ED[#for_i].Tiempos_TiempoAct := 0;
END_IF;
// =============================================================================
// RETARDO A LA DESACTIVACIÓN AUTOMÁTICO
IF NOT "DB2000_ED".ED[#for_i].Estado_EntradaCanal AND "DB2000_ED".ED[#for_i].Config_HabilitarRetardoDes THEN
IF #Pulso1seg AND "DB2000_ED".ED[#for_i].Tiempos_TiempoDes < "DB2000_ED".ED[#for_i].Tiempos_SPTiempoDes THEN
"DB2000_ED".ED[#for_i].Tiempos_TiempoDes += 1;
END_IF;
IF "DB2000_ED".ED[#for_i].Tiempos_TiempoDes >= "DB2000_ED".ED[#for_i].Tiempos_SPTiempoDes THEN
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
END_IF;
ELSIF NOT "DB2000_ED".ED[#for_i].Estado_EntradaCanal THEN
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
ELSE
"DB2000_ED".ED[#for_i].Tiempos_TiempoDes := 0;
END_IF;
END_IF;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2000_ED".ED[#for_i].Estado_AutoMan AND NOT "DB2000_ED".ED[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_10_DISP_ED",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_ED",
CodInt_2 := #for_i,
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 "DB2000_ED".ED[#for_i].Estado_AutoMan AND "DB2000_ED".ED[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_10_DISP_ED",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_ED",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2000_ED".ED[#for_i].Estado_AutoMan AND "DB2000_ED".ED[#for_i].Estado_Activado AND NOT "DB2000_ED".ED[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_10_DISP_ED",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_ED",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2000_ED".ED[#for_i].Estado_AutoMan AND NOT "DB2000_ED".ED[#for_i].Estado_Activado AND "DB2000_ED".ED[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_10_DISP_ED",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_ED",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION TRAZABILIDAD_MANUALIZACION
// =============================================================================
// ESTADO ANTERIOR MANUAL/AUTOMÁTICO
"DB2000_ED".ED[#for_i].Aux_oldAutoMan := "DB2000_ED".ED[#for_i].Estado_AutoMan;
"DB2000_ED".ED[#for_i].Aux_oldActivado := "DB2000_ED".ED[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2000_ED".ED[#for_i].Estado_EntradaCanal := FALSE;
"DB2000_ED".ED[#for_i].Estado_Activado := FALSE;
"DB2000_ED".ED[#for_i].Tiempos_TiempoAct := 0;
"DB2000_ED".ED[#for_i].Tiempos_TiempoDes := 0;
END_IF;
REGION ESTADO_HMI
// =============================================================================
// ESTADO BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2000_ED".ED[#for_i].Hmi_Estado.%X0 := "DB2000_ED".ED[#for_i].Config_Habilitar;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X1 := "DB2000_ED".ED[#for_i].Estado_AutoMan;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X2 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X3 := "DB2000_ED".ED[#for_i].Estado_Activado;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X8 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X13 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2000_ED".ED[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION ESTADO_HMI
END_FOR;
END_REGION
END_FUNCTION
@@ -0,0 +1,576 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_43c";
S7_BlockNumber := "2001";
S7_BlockTitle := "MLC_3fJ";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2001_ZC_EA" : Void
VAR_INPUT
{ S7_MLC := "MLC_4ba" }
Pulso1seg : Bool;
{ S7_MLC := "MLC_3tC" }
Simulacion : Bool;
{ S7_MLC := "MLC_UZ" }
Arranque : Bool;
{ S7_MLC := "MLC_52u" }
Ack : Bool;
{ S7_MLC := "MLC_mK" }
UsuarioActual : String;
{ S7_MLC := "MLC_3aC" }
FechaHoraActual : DTL;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_x0 : Real;
t_x1 : Real;
t_y0 : Real;
t_y1 : Real;
t_x : Real;
t_ErrorGeneral : Bool;
t_LecturaRealizada : Bool;
t_SubError : Real;
t_ValorTarjeta : Int;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO_ESCALADO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
TIPO_ACCESO_INDIRECTO_WORD_SIN_ESCALADO : SInt := 2;
TIPO_ACCESO_INDIRECTO_DWORD_SIN_ESCALADO : SInt := 3;
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 Entrada Analogica.
En ella se realizan las siguientes acciones:
- Se gestiona el multiplexado de los dispositivos para su uso en sistemas HMI/SCADA.
- Se realiza la lectura de la entrada de manera indirecta si esta habilitada.
- Se gestiona el escalado si esta habilitado.
- Se gestiona el estado del dispositivo
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | `DB2001_EA` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.00 | 15.10.2020 | (ABH) | Primera versión. |
| 00.00.01 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT en base al tipo de funcion.Se añade seguridad en el escalado. |
| 00.00.02 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.06 | 29.04.2022 | (HCR) | Cambio del estado de la señal, se agrega señal estado por prioridades. Cambio de las condiciones de para error de configuración. Cambio en la programaciíon de alarmas. Cambio en los límites de la señal |
| 00.00.07 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones |
| 00.00.08 | 20.12.2022 | (HCR) | Se añade trazabilidad de escalado y offset |
| 00.00.09 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.10 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado.nSe añade movimiento de EA[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.11 | 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. |
| 00.00.12 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.13 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.14 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.15 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 00.00.16 | 14.05.2026 | (ABH) | Se modifica UDT para añadir tipo de lectura. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2001_EA".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2001_EA".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2001_EA".NumDispositivos := "N_MAX_DISP_EA";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_EA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_EA";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
REGION LIMITE_GRUPO_ALARMA
IF "DB2001_EA".EA[#for_i].Config_GrupoAlarma < 0 THEN
"DB2001_EA".EA[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2001_EA".EA[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2001_EA".EA[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
#t_LecturaRealizada := FALSE;
#t_ErrorGeneral := FALSE;
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2001_EA".EA[#for_i].Config_Habilitar THEN
REGION LECTURA
CASE "DB2001_EA".EA[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO_ESCALADO:
REGION LECTURA_VALORES_DE_LA_TARJETA
IF "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte > 0 THEN
// =============================================================================
// LECTURA DE LA SEÑAL DE ENTRADA - PEEK_WORD (area) 16#1 para CPU 1500; 16#81 para CPU 1200
#t_ValorTarjeta := WORD_TO_INT(PEEK_WORD(area := 16#81, dbNumber := 0, byteOffset := "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte));
// =============================================================================
// COMPROBAMOS LOCALARMASENTE LA LECTURA CORRECTA DE LA ENTRADA ANALOGICA
REGION DESCRIPCION_GET_ERR_ID()
// La instrucción "Consultar ID de error locALARMASente" consulta si se han producido errores en un bloque. Suele tratarse de un error de acceso.
// si al ejecutar el bloque el sistema notifica errores de ejecución desde que se ejecutó La instrucción por última vez, la instrucción emite la ID del primer error ocurrido.
// La ID de error solo se puede guardar en operandos del tipo de datos WORD. Si se producen varios errores en el bloque, la instrucción soluciona el primer error ocurrido,
// y solo entonces la instrucción emite la ID de error del siguiente error ocurrido.
// 0 0 Ningún error
// 2503 9475 Puntero no válido
// 2520 9504 STRING no válido
// 2522 9506 Error de lectura: operando fuera del rango válido
// 2523 9507 Error de escritura: operando fuera del rango válido
// 2524 9508 Error de lectura: operando no válido
// 2525 9509 Error de escritura: operando no válido
// 2528 9512 Error de lectura: alineación de datos
// 2529 9513 Error de escritura: alineación de datos
// 252C 9516 Puntero no válido
// 2530 9520 Error de escritura: Bloque de datos
// 2533 9523 Referencia usada no válida
// 2538 9528 Error de acceso: el DB no existe
// 2539 9529 Error de acceso: se ha utilizado un DB incorrecto
// 253A 9530 El bloque de datos global no existe
// 253C 9532 Indicación errónea o la función no existe
// 253D 9533 La función de sistema no existe
// 253E 9534 Indicación errónea o el bloque de función no existe
// 253F 9535 El bloque de sistema no existe
// 2550 9552 Error de acceso: el DB no existe
// 2551 9553 Error de acceso: se ha utilizado un DB incorrecto
// 2575 9589 Error en la profundidad de anidamiento del programa
// 2576 9590 Error en la distribución de datos locales
// 2577 9591 La propiedad de bloque "Alimentación de parámetros a través de registros" no está activada.
// 25A0 9632 Error interno en TP
// 25A1 9633 Variable protegida contra escritura
// 25A2 9634 Valor numérico no válido de la variable
// 2942 10562 Error de lectura: entrada
// 2943 10563 Error de escritura: salida
END_REGION
"DB2001_EA".EA[#for_i].Estado_Lectura := GET_ERR_ID();
#t_LecturaRealizada := ("DB2001_EA".EA[#for_i].Estado_Lectura = 0);
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := #t_ValorTarjeta;
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := 0;
#t_LecturaRealizada := TRUE;
END_IF;
END_REGION LECTURA_VALORES_DE_LA_TARJETA
REGION CALCULO_VALOR
IF "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte > 0 THEN
IF "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria > "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria AND
"DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta > "DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta THEN
#t_x0 := INT_TO_REAL("DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta);
#t_x1 := INT_TO_REAL("DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta);
#t_x := DINT_TO_REAL("DB2001_EA".EA[#for_i].Estado_ValorTarjeta);
#t_y0 := "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria;
#t_y1 := "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria;
// =============================================================================
// DETECCIÓN DE FALLO EN EL CANAL
IF ("DB2001_EA".EA[#for_i].Estado_ValorTarjeta > -32768) AND ("DB2001_EA".EA[#for_i].Estado_ValorTarjeta < 32767) AND #t_LecturaRealizada THEN
IF ("DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria <> "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria)
AND
("DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta <> "DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta)
THEN
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := ((#t_x - #t_x0) * ((#t_y1 - #t_y0) / (#t_x1 - #t_x0))) + #t_y0;
// =============================================================================
// LÍMITES
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria > "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria THEN
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria;
END_IF;
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria < "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria THEN
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria;
END_IF;
// =============================================================================
// OFFSET
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := "DB2001_EA".EA[#for_i].Estado_ValorIngenieria + "DB2001_EA".EA[#for_i].Config_EscaladoOffset;
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0.0;
END_IF;
"DB2001_EA".EA[#for_i].Alarmas_HiloRoto := FALSE;
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0.0;
"DB2001_EA".EA[#for_i].Alarmas_HiloRoto := TRUE; // Error del canal
END_IF;
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0.0;
END_IF;
END_IF;
END_REGION CALCULO_VALOR
#TIPO_ACCESO_INDIRECTO_WORD_SIN_ESCALADO:
REGION LECTURA_VALORES_DE_LA_TARJETA
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := 0;
IF "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte > 0 THEN
// =============================================================================
// LECTURA DE LA SEÑAL DE ENTRADA - PEEK_WORD (area) 16#1 para CPU 1500; 16#81 para CPU 1200
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := DINT_TO_REAL(WORD_TO_DINT(PEEK_WORD(area := 16#81, dbNumber := 0, byteOffset := "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte)));
// =============================================================================
// COMPROBAMOS LOCALARMASENTE LA LECTURA CORRECTA DE LA ENTRADA ANALOGICA
"DB2001_EA".EA[#for_i].Estado_Lectura := GET_ERR_ID();
#t_LecturaRealizada := ("DB2001_EA".EA[#for_i].Estado_Lectura = 0);
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0;
#t_LecturaRealizada := TRUE;
END_IF;
END_REGION LECTURA_VALORES_DE_LA_TARJETA
#TIPO_ACCESO_INDIRECTO_DWORD_SIN_ESCALADO:
REGION LECTURA_VALORES_DE_LA_TARJETA
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := 0;
IF "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte > 0 THEN
// =============================================================================
// LECTURA DE LA SEÑAL DE ENTRADA - PEEK_WORD (area) 16#1 para CPU 1500; 16#81 para CPU 1200
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := DINT_TO_REAL(DWORD_TO_DINT(PEEK_DWORD(area := 16#81, dbNumber := 0, byteOffset := "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte)));
// =============================================================================
// COMPROBAMOS LOCALARMASENTE LA LECTURA CORRECTA DE LA ENTRADA ANALOGICA
"DB2001_EA".EA[#for_i].Estado_Lectura := GET_ERR_ID();
#t_LecturaRealizada := ("DB2001_EA".EA[#for_i].Estado_Lectura = 0);
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0;
#t_LecturaRealizada := TRUE;
END_IF;
END_REGION LECTURA_VALORES_DE_LA_TARJETA
#TIPO_ACCESO_PLC:
#t_LecturaRealizada := TRUE;
END_CASE;
END_REGION LECTURA
REGION MODOS_DE_TRABAJO
// =============================================================================
// FORZADO DE LA SEÑAL
IF "DB2001_EA".EA[#for_i].Estado_AutoMan THEN
"DB2001_EA".Agrup["DB2001_EA".EA[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := "DB2001_EA".EA[#for_i].Orden_ConsignaManual;
ELSE
"DB2001_EA".EA[#for_i].Orden_ConsignaManual := "DB2001_EA".EA[#for_i].Estado_ValorIngenieria;
END_IF;
END_REGION
REGION ERRORES
#t_SubError := "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria - "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria;
// =============================================================================
// ALARMA NIVEL MÍNIMO
IF "DB2001_EA".EA[#for_i].Config_HabilitarAlarmaMin THEN
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria < "DB2001_EA".EA[#for_i].Config_ErrorMinIngenieria THEN
"DB2001_EA".EA[#for_i].Alarmas_Min := TRUE;
END_IF;
// =============================================================================
// RESET DE ALARMA CON HISTÉRESIS EN UNIDADES DE INGENIERÍA
IF NOT ("DB2001_EA".EA[#for_i].Config_HisteresisTipo) AND ("DB2001_EA".EA[#for_i].Estado_ValorIngenieria >= ("DB2001_EA".EA[#for_i].Config_ErrorMinIngenieria + "DB2001_EA".EA[#for_i].Config_ErrorMinHisteresis)) THEN
"DB2001_EA".EA[#for_i].Alarmas_Min := FALSE;
END_IF;
// =============================================================================
// RESET DE ALARMA CON HISTÉRESIS EN %
IF "DB2001_EA".EA[#for_i].Config_HisteresisTipo AND ("DB2001_EA".EA[#for_i].Estado_ValorIngenieria >= "DB2001_EA".EA[#for_i].Config_ErrorMinIngenieria + ("DB2001_EA".EA[#for_i].Config_ErrorMinHisteresis / 100.0) * #t_SubError) THEN
"DB2001_EA".EA[#for_i].Alarmas_Min := FALSE;
END_IF;
ELSE
"DB2001_EA".EA[#for_i].Alarmas_Min := FALSE;
END_IF;
// =============================================================================
// ALARMA NIVEL MÁXIMO
IF "DB2001_EA".EA[#for_i].Config_HabilitarAlarmaMax THEN
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria > "DB2001_EA".EA[#for_i].Config_ErrorMaxIngenieria THEN
"DB2001_EA".EA[#for_i].Alarmas_Max := TRUE;
END_IF;
// =============================================================================
// RESET DE ALARMA CON HISTÉRESIS EN UNIDADES DE INGENIERÍA
IF NOT ("DB2001_EA".EA[#for_i].Config_HisteresisTipo) AND ("DB2001_EA".EA[#for_i].Estado_ValorIngenieria <= ("DB2001_EA".EA[#for_i].Config_ErrorMaxIngenieria - "DB2001_EA".EA[#for_i].Config_ErrorMaxHisteresis)) THEN
"DB2001_EA".EA[#for_i].Alarmas_Max := FALSE;
END_IF;
// =============================================================================
// RESET DE ALARMA CON HISTÉRESIS EN %
IF "DB2001_EA".EA[#for_i].Config_HisteresisTipo AND ("DB2001_EA".EA[#for_i].Estado_ValorIngenieria <= "DB2001_EA".EA[#for_i].Config_ErrorMaxIngenieria - ("DB2001_EA".EA[#for_i].Config_ErrorMaxHisteresis / 100.0) * #t_SubError) THEN
"DB2001_EA".EA[#for_i].Alarmas_Max := FALSE;
END_IF;
ELSE
"DB2001_EA".EA[#for_i].Alarmas_Max := FALSE;
END_IF;
#t_ErrorGeneral := "DB2001_EA".EA[#for_i].Alarmas_HiloRoto OR "DB2001_EA".EA[#for_i].Alarmas_Max OR "DB2001_EA".EA[#for_i].Alarmas_Min;
"DB2001_EA".EA[#for_i].Alarmas_General := #t_ErrorGeneral;
IF #t_ErrorGeneral THEN
"DB2001_EA".Agrup["DB2001_EA".EA[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
END_IF;
"DB2001_EA".EA[#for_i].Alarmas_Lectura := NOT #t_LecturaRealizada;
END_REGION
REGION ESTADO
// Estado normal
"DB2001_EA".EA[#for_i].Aux_Estado := 1;
// Error de configuración
IF ("DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria = "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria) OR
("DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta = "DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta)
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 6;
END_IF;
// Error de configuración
IF ("DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria < "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria) OR
("DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta < "DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta)
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 6;
END_IF;
// Error del canal
IF "DB2001_EA".EA[#for_i].Config_TipoAcceso = #TIPO_ACCESO_INDIRECTO_ESCALADO AND
("DB2001_EA".EA[#for_i].Estado_ValorTarjeta <= -32766) OR
("DB2001_EA".EA[#for_i].Estado_ValorTarjeta >= 32766)
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 3;
END_IF;
// Señal en alarma
IF "DB2001_EA".EA[#for_i].Config_HabilitarAlarmaMin THEN
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria < "DB2001_EA".EA[#for_i].Config_ErrorMinIngenieria
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 4;
END_IF;
END_IF;
// Señal en alarma
IF "DB2001_EA".EA[#for_i].Config_HabilitarAlarmaMax THEN
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria > "DB2001_EA".EA[#for_i].Config_ErrorMaxIngenieria
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 5;
END_IF;
END_IF;
// Error de lectura
IF NOT #t_LecturaRealizada THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 7;
END_IF;
// EA no direccionada
IF ("DB2001_EA".EA[#for_i].Config_TipoAcceso = #TIPO_ACCESO_INDIRECTO_ESCALADO OR "DB2001_EA".EA[#for_i].Config_TipoAcceso = #TIPO_ACCESO_INDIRECTO_ESCALADO)
AND
"DB2001_EA".EA[#for_i].Config_DireccionEntradaByte <= 0
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 8;
END_IF;
// Señal forzada
IF "DB2001_EA".EA[#for_i].Estado_AutoMan THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 2;
END_IF;
END_REGION
REGION TRAZABILIDAD
IF "DB2001_EA".EA[#for_i].Estado_AutoMan AND NOT "DB2001_EA".EA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_11_DISP_EA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_EA",
CodInt_2 := #for_i,
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 "DB2001_EA".EA[#for_i].Estado_AutoMan AND "DB2001_EA".EA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_11_DISP_EA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_EA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2001_EA".EA[#for_i].Aux_oldAutoMan := "DB2001_EA".EA[#for_i].Estado_AutoMan;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := 0;
"DB2001_EA".EA[#for_i].Hmi_Estado := 0; // Canal deshabilitado
"DB2001_EA".EA[#for_i].Aux_Estado := 0; // Canal deshabilitado
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0.0;
"DB2001_EA".EA[#for_i].Alarmas_Max := FALSE;
"DB2001_EA".EA[#for_i].Alarmas_Min := FALSE;
"DB2001_EA".EA[#for_i].Alarmas_HiloRoto := FALSE;
"DB2001_EA".EA[#for_i].Alarmas_General := FALSE;
"DB2001_EA".EA[#for_i].Estado_AutoMan := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Reserva
// Bit 3 Reserva
// Bit 4 Reserva
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma Hilo Roto
// Bit 9 Alarma Alta
// Bit 10 Alarma Baja
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma 5
// Bit 14 Reserva
// Bit 15 Reserva
"DB2001_EA".EA[#for_i].Hmi_Estado.%X0 := "DB2001_EA".EA[#for_i].Config_Habilitar;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X1 := "DB2001_EA".EA[#for_i].Estado_AutoMan;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X2 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X3 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X8 := "DB2001_EA".EA[#for_i].Alarmas_HiloRoto;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X9 := "DB2001_EA".EA[#for_i].Alarmas_Max;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X10 := "DB2001_EA".EA[#for_i].Alarmas_Min;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X13 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2001_EA".EA[#for_i].Alarmas_General AND NOT "DB2001_EA".EA[#for_i].Aux_oldAlarma THEN
"DB2001_EA".Agrup["DB2001_EA".EA[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2001_EA".EA[#for_i].Aux_oldAlarma := "DB2001_EA".EA[#for_i].Alarmas_General;
END_REGION
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2001_EA".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION
END_NETWORK
END_FUNCTION
@@ -0,0 +1,17 @@
MultiLingualTexts:
- id: MLC_3fJ
es-ES: FC2001_EA
- id: MLC_43c
es-ES: Modelo de dispositivo entrada analogica
- id: MLC_4ba
es-ES: Pulso de 1 segundo
- id: MLC_3tC
es-ES: Simulacion
- id: MLC_UZ
es-ES: Primer arranque
- id: MLC_52u
es-ES: Acuse general
- id: MLC_mK
es-ES: Usuario actual
- id: MLC_3aC
es-ES: Fecha y hora actual
@@ -1,568 +0,0 @@
FUNCTION "FC2001_ZC_EA" : Void
TITLE = FC2001_EA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Modelo de dispositivo entrada analogica
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
Simulacion : Bool; // Simulacion
Arranque : Bool; // Primer arranque
Ack : Bool; // Acuse general
UsuarioActual : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_x0 : Real;
t_x1 : Real;
t_y0 : Real;
t_y1 : Real;
t_x : Real;
t_ErrorGeneral : Bool;
t_LecturaRealizada : Bool;
t_SubError : Real;
t_ValorTarjeta : Int;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO_ESCALADO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
TIPO_ACCESO_INDIRECTO_WORD_SIN_ESCALADO : SInt := 2;
TIPO_ACCESO_INDIRECTO_DWORD_SIN_ESCALADO : SInt := 3;
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 Entrada Analogica.
En ella se realizan las siguientes acciones:
- Se gestiona el multiplexado de los dispositivos para su uso en sistemas HMI/SCADA.
- Se realiza la lectura de la entrada de manera indirecta si esta habilitada.
- Se gestiona el escalado si esta habilitado.
- Se gestiona el estado del dispositivo
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | `DB2001_EA` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.00 | 15.10.2020 | (ABH) | Primera versión. |
| 00.00.01 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT en base al tipo de funcion.Se añade seguridad en el escalado. |
| 00.00.02 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.06 | 29.04.2022 | (HCR) | Cambio del estado de la señal, se agrega señal estado por prioridades. Cambio de las condiciones de para error de configuración. Cambio en la programaciíon de alarmas. Cambio en los límites de la señal |
| 00.00.07 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones |
| 00.00.08 | 20.12.2022 | (HCR) | Se añade trazabilidad de escalado y offset |
| 00.00.09 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.10 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado.nSe añade movimiento de EA[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.11 | 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. |
| 00.00.12 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.13 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.14 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.15 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 00.00.16 | 14.05.2026 | (ABH) | Se modifica UDT para añadir tipo de lectura. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2001_EA".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2001_EA".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2001_EA".NumDispositivos := "N_MAX_DISP_EA";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_EA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_EA";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
REGION LIMITE_GRUPO_ALARMA
IF "DB2001_EA".EA[#for_i].Config_GrupoAlarma < 0 THEN
"DB2001_EA".EA[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2001_EA".EA[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2001_EA".EA[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
#t_LecturaRealizada := FALSE;
#t_ErrorGeneral := FALSE;
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2001_EA".EA[#for_i].Config_Habilitar THEN
REGION LECTURA
CASE "DB2001_EA".EA[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO_ESCALADO:
REGION LECTURA_VALORES_DE_LA_TARJETA
IF "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte > 0 THEN
// =============================================================================
// LECTURA DE LA SEÑAL DE ENTRADA - PEEK_WORD (area) 16#1 para CPU 1500; 16#81 para CPU 1200
#t_ValorTarjeta := WORD_TO_INT(PEEK_WORD(area := 16#81, dbNumber := 0, byteOffset := "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte));
// =============================================================================
// COMPROBAMOS LOCALARMASENTE LA LECTURA CORRECTA DE LA ENTRADA ANALOGICA
REGION DESCRIPCION_GET_ERR_ID()
// La instrucción "Consultar ID de error locALARMASente" consulta si se han producido errores en un bloque. Suele tratarse de un error de acceso.
// si al ejecutar el bloque el sistema notifica errores de ejecución desde que se ejecutó La instrucción por última vez, la instrucción emite la ID del primer error ocurrido.
// La ID de error solo se puede guardar en operandos del tipo de datos WORD. Si se producen varios errores en el bloque, la instrucción soluciona el primer error ocurrido,
// y solo entonces la instrucción emite la ID de error del siguiente error ocurrido.
// 0 0 Ningún error
// 2503 9475 Puntero no válido
// 2520 9504 STRING no válido
// 2522 9506 Error de lectura: operando fuera del rango válido
// 2523 9507 Error de escritura: operando fuera del rango válido
// 2524 9508 Error de lectura: operando no válido
// 2525 9509 Error de escritura: operando no válido
// 2528 9512 Error de lectura: alineación de datos
// 2529 9513 Error de escritura: alineación de datos
// 252C 9516 Puntero no válido
// 2530 9520 Error de escritura: Bloque de datos
// 2533 9523 Referencia usada no válida
// 2538 9528 Error de acceso: el DB no existe
// 2539 9529 Error de acceso: se ha utilizado un DB incorrecto
// 253A 9530 El bloque de datos global no existe
// 253C 9532 Indicación errónea o la función no existe
// 253D 9533 La función de sistema no existe
// 253E 9534 Indicación errónea o el bloque de función no existe
// 253F 9535 El bloque de sistema no existe
// 2550 9552 Error de acceso: el DB no existe
// 2551 9553 Error de acceso: se ha utilizado un DB incorrecto
// 2575 9589 Error en la profundidad de anidamiento del programa
// 2576 9590 Error en la distribución de datos locales
// 2577 9591 La propiedad de bloque "Alimentación de parámetros a través de registros" no está activada.
// 25A0 9632 Error interno en TP
// 25A1 9633 Variable protegida contra escritura
// 25A2 9634 Valor numérico no válido de la variable
// 2942 10562 Error de lectura: entrada
// 2943 10563 Error de escritura: salida
END_REGION
"DB2001_EA".EA[#for_i].Estado_Lectura := GET_ERR_ID();
#t_LecturaRealizada := ("DB2001_EA".EA[#for_i].Estado_Lectura = 0);
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := #t_ValorTarjeta;
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := 0;
#t_LecturaRealizada := TRUE;
END_IF;
END_REGION LECTURA_VALORES_DE_LA_TARJETA
REGION CALCULO_VALOR
IF "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte > 0 THEN
IF "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria > "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria AND
"DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta > "DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta THEN
#t_x0 := INT_TO_REAL("DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta);
#t_x1 := INT_TO_REAL("DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta);
#t_x := DINT_TO_REAL("DB2001_EA".EA[#for_i].Estado_ValorTarjeta);
#t_y0 := "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria;
#t_y1 := "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria;
// =============================================================================
// DETECCIÓN DE FALLO EN EL CANAL
IF ("DB2001_EA".EA[#for_i].Estado_ValorTarjeta > -32768) AND ("DB2001_EA".EA[#for_i].Estado_ValorTarjeta < 32767) AND #t_LecturaRealizada THEN
IF ("DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria <> "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria)
AND
("DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta <> "DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta)
THEN
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := ((#t_x - #t_x0) * ((#t_y1 - #t_y0) / (#t_x1 - #t_x0))) + #t_y0;
// =============================================================================
// LÍMITES
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria > "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria THEN
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria;
END_IF;
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria < "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria THEN
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria;
END_IF;
// =============================================================================
// OFFSET
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := "DB2001_EA".EA[#for_i].Estado_ValorIngenieria + "DB2001_EA".EA[#for_i].Config_EscaladoOffset;
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0.0;
END_IF;
"DB2001_EA".EA[#for_i].Alarmas_HiloRoto := FALSE;
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0.0;
"DB2001_EA".EA[#for_i].Alarmas_HiloRoto := TRUE; // Error del canal
END_IF;
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0.0;
END_IF;
END_IF;
END_REGION CALCULO_VALOR
#TIPO_ACCESO_INDIRECTO_WORD_SIN_ESCALADO:
REGION LECTURA_VALORES_DE_LA_TARJETA
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := 0;
IF "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte > 0 THEN
// =============================================================================
// LECTURA DE LA SEÑAL DE ENTRADA - PEEK_WORD (area) 16#1 para CPU 1500; 16#81 para CPU 1200
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := DINT_TO_REAL(WORD_TO_DINT(PEEK_WORD(area := 16#81, dbNumber := 0, byteOffset := "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte)));
// =============================================================================
// COMPROBAMOS LOCALARMASENTE LA LECTURA CORRECTA DE LA ENTRADA ANALOGICA
"DB2001_EA".EA[#for_i].Estado_Lectura := GET_ERR_ID();
#t_LecturaRealizada := ("DB2001_EA".EA[#for_i].Estado_Lectura = 0);
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0;
#t_LecturaRealizada := TRUE;
END_IF;
END_REGION LECTURA_VALORES_DE_LA_TARJETA
#TIPO_ACCESO_INDIRECTO_DWORD_SIN_ESCALADO:
REGION LECTURA_VALORES_DE_LA_TARJETA
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := 0;
IF "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte > 0 THEN
// =============================================================================
// LECTURA DE LA SEÑAL DE ENTRADA - PEEK_WORD (area) 16#1 para CPU 1500; 16#81 para CPU 1200
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := DINT_TO_REAL(DWORD_TO_DINT(PEEK_DWORD(area := 16#81, dbNumber := 0, byteOffset := "DB2001_EA".EA[#for_i].Config_DireccionEntradaByte)));
// =============================================================================
// COMPROBAMOS LOCALARMASENTE LA LECTURA CORRECTA DE LA ENTRADA ANALOGICA
"DB2001_EA".EA[#for_i].Estado_Lectura := GET_ERR_ID();
#t_LecturaRealizada := ("DB2001_EA".EA[#for_i].Estado_Lectura = 0);
ELSE
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0;
#t_LecturaRealizada := TRUE;
END_IF;
END_REGION LECTURA_VALORES_DE_LA_TARJETA
#TIPO_ACCESO_PLC:
#t_LecturaRealizada := TRUE;
END_CASE;
END_REGION LECTURA
REGION MODOS_DE_TRABAJO
// =============================================================================
// FORZADO DE LA SEÑAL
IF "DB2001_EA".EA[#for_i].Estado_AutoMan THEN
"DB2001_EA".Agrup["DB2001_EA".EA[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := "DB2001_EA".EA[#for_i].Orden_ConsignaManual;
ELSE
"DB2001_EA".EA[#for_i].Orden_ConsignaManual := "DB2001_EA".EA[#for_i].Estado_ValorIngenieria;
END_IF;
END_REGION
REGION ERRORES
#t_SubError := "DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria - "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria;
// =============================================================================
// ALARMA NIVEL MÍNIMO
IF "DB2001_EA".EA[#for_i].Config_HabilitarAlarmaMin THEN
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria < "DB2001_EA".EA[#for_i].Config_ErrorMinIngenieria THEN
"DB2001_EA".EA[#for_i].Alarmas_Min := TRUE;
END_IF;
// =============================================================================
// RESET DE ALARMA CON HISTÉRESIS EN UNIDADES DE INGENIERÍA
IF NOT ("DB2001_EA".EA[#for_i].Config_HisteresisTipo) AND ("DB2001_EA".EA[#for_i].Estado_ValorIngenieria >= ("DB2001_EA".EA[#for_i].Config_ErrorMinIngenieria + "DB2001_EA".EA[#for_i].Config_ErrorMinHisteresis)) THEN
"DB2001_EA".EA[#for_i].Alarmas_Min := FALSE;
END_IF;
// =============================================================================
// RESET DE ALARMA CON HISTÉRESIS EN %
IF "DB2001_EA".EA[#for_i].Config_HisteresisTipo AND ("DB2001_EA".EA[#for_i].Estado_ValorIngenieria >= "DB2001_EA".EA[#for_i].Config_ErrorMinIngenieria + ("DB2001_EA".EA[#for_i].Config_ErrorMinHisteresis / 100.0) * #t_SubError) THEN
"DB2001_EA".EA[#for_i].Alarmas_Min := FALSE;
END_IF;
ELSE
"DB2001_EA".EA[#for_i].Alarmas_Min := FALSE;
END_IF;
// =============================================================================
// ALARMA NIVEL MÁXIMO
IF "DB2001_EA".EA[#for_i].Config_HabilitarAlarmaMax THEN
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria > "DB2001_EA".EA[#for_i].Config_ErrorMaxIngenieria THEN
"DB2001_EA".EA[#for_i].Alarmas_Max := TRUE;
END_IF;
// =============================================================================
// RESET DE ALARMA CON HISTÉRESIS EN UNIDADES DE INGENIERÍA
IF NOT ("DB2001_EA".EA[#for_i].Config_HisteresisTipo) AND ("DB2001_EA".EA[#for_i].Estado_ValorIngenieria <= ("DB2001_EA".EA[#for_i].Config_ErrorMaxIngenieria - "DB2001_EA".EA[#for_i].Config_ErrorMaxHisteresis)) THEN
"DB2001_EA".EA[#for_i].Alarmas_Max := FALSE;
END_IF;
// =============================================================================
// RESET DE ALARMA CON HISTÉRESIS EN %
IF "DB2001_EA".EA[#for_i].Config_HisteresisTipo AND ("DB2001_EA".EA[#for_i].Estado_ValorIngenieria <= "DB2001_EA".EA[#for_i].Config_ErrorMaxIngenieria - ("DB2001_EA".EA[#for_i].Config_ErrorMaxHisteresis / 100.0) * #t_SubError) THEN
"DB2001_EA".EA[#for_i].Alarmas_Max := FALSE;
END_IF;
ELSE
"DB2001_EA".EA[#for_i].Alarmas_Max := FALSE;
END_IF;
#t_ErrorGeneral := "DB2001_EA".EA[#for_i].Alarmas_HiloRoto OR "DB2001_EA".EA[#for_i].Alarmas_Max OR "DB2001_EA".EA[#for_i].Alarmas_Min;
"DB2001_EA".EA[#for_i].Alarmas_General := #t_ErrorGeneral;
IF #t_ErrorGeneral THEN
"DB2001_EA".Agrup["DB2001_EA".EA[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
END_IF;
"DB2001_EA".EA[#for_i].Alarmas_Lectura := NOT #t_LecturaRealizada;
END_REGION
REGION ESTADO
// Estado normal
"DB2001_EA".EA[#for_i].Aux_Estado := 1;
// Error de configuración
IF ("DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria = "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria) OR
("DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta = "DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta)
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 6;
END_IF;
// Error de configuración
IF ("DB2001_EA".EA[#for_i].Config_EscaladoMaxIngenieria < "DB2001_EA".EA[#for_i].Config_EscaladoMinIngenieria) OR
("DB2001_EA".EA[#for_i].Config_EscaladoMaxTarjeta < "DB2001_EA".EA[#for_i].Config_EscaladoMinTarjeta)
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 6;
END_IF;
// Error del canal
IF "DB2001_EA".EA[#for_i].Config_TipoAcceso = #TIPO_ACCESO_INDIRECTO_ESCALADO AND
("DB2001_EA".EA[#for_i].Estado_ValorTarjeta <= -32766) OR
("DB2001_EA".EA[#for_i].Estado_ValorTarjeta >= 32766)
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 3;
END_IF;
// Señal en alarma
IF "DB2001_EA".EA[#for_i].Config_HabilitarAlarmaMin THEN
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria < "DB2001_EA".EA[#for_i].Config_ErrorMinIngenieria
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 4;
END_IF;
END_IF;
// Señal en alarma
IF "DB2001_EA".EA[#for_i].Config_HabilitarAlarmaMax THEN
IF "DB2001_EA".EA[#for_i].Estado_ValorIngenieria > "DB2001_EA".EA[#for_i].Config_ErrorMaxIngenieria
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 5;
END_IF;
END_IF;
// Error de lectura
IF NOT #t_LecturaRealizada THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 7;
END_IF;
// EA no direccionada
IF ("DB2001_EA".EA[#for_i].Config_TipoAcceso = #TIPO_ACCESO_INDIRECTO_ESCALADO OR "DB2001_EA".EA[#for_i].Config_TipoAcceso = #TIPO_ACCESO_INDIRECTO_ESCALADO)
AND
"DB2001_EA".EA[#for_i].Config_DireccionEntradaByte <= 0
THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 8;
END_IF;
// Señal forzada
IF "DB2001_EA".EA[#for_i].Estado_AutoMan THEN
"DB2001_EA".EA[#for_i].Aux_Estado := 2;
END_IF;
END_REGION
REGION TRAZABILIDAD
IF "DB2001_EA".EA[#for_i].Estado_AutoMan AND NOT "DB2001_EA".EA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_11_DISP_EA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_EA",
CodInt_2 := #for_i,
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 "DB2001_EA".EA[#for_i].Estado_AutoMan AND "DB2001_EA".EA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_11_DISP_EA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_EA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2001_EA".EA[#for_i].Aux_oldAutoMan := "DB2001_EA".EA[#for_i].Estado_AutoMan;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2001_EA".EA[#for_i].Estado_ValorTarjeta := 0;
"DB2001_EA".EA[#for_i].Hmi_Estado := 0; // Canal deshabilitado
"DB2001_EA".EA[#for_i].Aux_Estado := 0; // Canal deshabilitado
"DB2001_EA".EA[#for_i].Estado_ValorIngenieria := 0.0;
"DB2001_EA".EA[#for_i].Alarmas_Max := FALSE;
"DB2001_EA".EA[#for_i].Alarmas_Min := FALSE;
"DB2001_EA".EA[#for_i].Alarmas_HiloRoto := FALSE;
"DB2001_EA".EA[#for_i].Alarmas_General := FALSE;
"DB2001_EA".EA[#for_i].Estado_AutoMan := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Reserva
// Bit 3 Reserva
// Bit 4 Reserva
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma Hilo Roto
// Bit 9 Alarma Alta
// Bit 10 Alarma Baja
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma 5
// Bit 14 Reserva
// Bit 15 Reserva
"DB2001_EA".EA[#for_i].Hmi_Estado.%X0 := "DB2001_EA".EA[#for_i].Config_Habilitar;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X1 := "DB2001_EA".EA[#for_i].Estado_AutoMan;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X2 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X3 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X8 := "DB2001_EA".EA[#for_i].Alarmas_HiloRoto;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X9 := "DB2001_EA".EA[#for_i].Alarmas_Max;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X10 := "DB2001_EA".EA[#for_i].Alarmas_Min;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X13 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2001_EA".EA[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2001_EA".EA[#for_i].Alarmas_General AND NOT "DB2001_EA".EA[#for_i].Aux_oldAlarma THEN
"DB2001_EA".Agrup["DB2001_EA".EA[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2001_EA".EA[#for_i].Aux_oldAlarma := "DB2001_EA".EA[#for_i].Alarmas_General;
END_REGION
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2001_EA".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION
END_FUNCTION
@@ -0,0 +1,420 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4ii";
S7_BlockNumber := "2005";
S7_BlockTitle := "MLC_3pw";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2005_ZC_SD" : Void
VAR_INPUT
{ S7_MLC := "MLC_36J" }
Pulso1seg : Bool;
{ S7_MLC := "MLC_hm" }
Simulacion : Bool;
{ S7_MLC := "MLC_4ik" }
Arranque : Bool;
{ S7_MLC := "MLC_4ps" }
Ack : Bool;
{ S7_MLC := "MLC_3d4" }
SeguridadesOK : Bool;
{ S7_MLC := "MLC_4tF" }
UsuarioActual : String;
{ S7_MLC := "MLC_4ph" }
FechaHoraActual : DTL;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_ActivarConRetardo : Bool;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Salida Digital.
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 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 |
|----------|------------|---------|---------|
| 00.00.01 | 26.04.2022 | (HCR) | Primera version. Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. |
| 00.00.02 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.03 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones |
| 00.00.03 | 08.11.2022 | (HCR) | Se añade trazabilidad de activación manual |
| 00.00.04 | 20.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.05 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado Se añade movimiento de ED[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. |
| 00.00.08 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.09 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.10 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.11 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 00.00.12 | 14.05.2026 | (ABH) | Se modifica UDT para añadir tipo de lectura. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. Se corrigen logicas de activacion y se añade variable "Estado_Salida". |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2005_SD".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2005_SD".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2005_SD".NumDispositivos := "N_MAX_DISP_SD";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_SD" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_SD";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2005_SD".SD[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2005_SD".SD[#for_i].Config_GrupoAlarma < 0 THEN
"DB2005_SD".SD[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2005_SD".SD[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2005_SD".SD[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2005_SD".SD[#for_i].Estado_AutoMan THEN
"DB2005_SD".Agrup["DB2005_SD".SD[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2005_SD".SD[#for_i].Orden_ActivarManual AND #SeguridadesOK AND "DB2005_SD".SD[#for_i].Estado_SeguridadOk THEN // En caso de enclavar válvula en manual AND NOT "DB2005_SD".SD[#for_i].Estado_Enclavado
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
"DB2005_SD".SD[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2005_SD".SD[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2005_SD".SD[#for_i].Estado_AutoMan THEN
IF "DB2005_SD".SD[#for_i].Orden_ActivarAuto AND #SeguridadesOK AND "DB2005_SD".SD[#for_i].Estado_SeguridadOk AND NOT "DB2005_SD".SD[#for_i].Estado_Enclavado THEN
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
END_IF;
"DB2005_SD".SD[#for_i].Orden_ActivarManual := "DB2005_SD".SD[#for_i].Estado_Activado;
END_IF;
// Reset orden auto
"DB2005_SD".SD[#for_i].Orden_ActivarAuto := FALSE;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// SALIDA SIMULADA
IF #Simulacion THEN
IF #t_Activar THEN
"DB2005_SD".SD[#for_i].Estado_Activado := TRUE;
ELSE
"DB2005_SD".SD[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
IF NOT "DB2005_SD".SD[#for_i].Estado_AutoMan AND NOT #Simulacion THEN
// =============================================================================
// RETARDO A LA ACTIVACIÓN AUTOMÁTICO
IF #t_Activar AND "DB2005_SD".SD[#for_i].Config_HabilitarRetardoAct THEN
IF #Pulso1seg AND "DB2005_SD".SD[#for_i].Tiempos_TiempoAct < "DB2005_SD".SD[#for_i].Tiempos_SetPointTiempoAct THEN
"DB2005_SD".SD[#for_i].Tiempos_TiempoAct += 1;
END_IF;
IF "DB2005_SD".SD[#for_i].Tiempos_TiempoAct >= "DB2005_SD".SD[#for_i].Tiempos_SetPointTiempoAct THEN
#t_ActivarConRetardo := TRUE;
ELSE
#t_ActivarConRetardo := FALSE;
END_IF;
ELSIF #t_Activar THEN
#t_ActivarConRetardo := TRUE;
ELSE
"DB2005_SD".SD[#for_i].Tiempos_TiempoAct := 0;
END_IF;
// =============================================================================
// RETARDO A LA DESACTIVACIÓN AUTOMÁTICO
IF NOT #t_Activar AND "DB2005_SD".SD[#for_i].Config_HabilitarRetardoDes THEN
IF #Pulso1seg AND "DB2005_SD".SD[#for_i].Tiempos_TiempoDes < "DB2005_SD".SD[#for_i].Tiempos_SetPointTiempoDes THEN
"DB2005_SD".SD[#for_i].Tiempos_TiempoDes += 1;
END_IF;
IF "DB2005_SD".SD[#for_i].Tiempos_TiempoDes >= "DB2005_SD".SD[#for_i].Tiempos_SetPointTiempoDes THEN
#t_ActivarConRetardo := FALSE;
ELSE
#t_ActivarConRetardo := TRUE;
END_IF;
ELSIF NOT #t_Activar THEN
#t_ActivarConRetardo := FALSE;
ELSE
"DB2005_SD".SD[#for_i].Tiempos_TiempoDes := 0;
END_IF;
ELSE
#t_ActivarConRetardo := #t_Activar;
END_IF;
// Estado activado
"DB2005_SD".SD[#for_i].Estado_Activado := #t_ActivarConRetardo;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION ESCRITURA_SALIDA
// =============================================================================
// INVERSION DE LA SALIDA
IF "DB2005_SD".SD[#for_i].Config_HabilitarInvertir THEN
#t_ActivarConRetardo := NOT #t_ActivarConRetardo;
END_IF;
CASE "DB2005_SD".SD[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA VALORES DE LA TARJETA
IF "DB2005_SD".SD[#for_i].Config_DireccionSalidaByte >= 0 THEN
IF #t_ActivarConRetardo THEN
"DB2005_SD".SD[#for_i].Estado_Salida := TRUE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2005_SD".SD[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2005_SD".SD[#for_i].Config_DireccionSalidaBit,
value := TRUE);
ELSE
"DB2005_SD".SD[#for_i].Estado_Salida := FALSE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2005_SD".SD[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2005_SD".SD[#for_i].Config_DireccionSalidaBit,
value := FALSE);
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
IF #t_ActivarConRetardo THEN
"DB2005_SD".SD[#for_i].Estado_Salida := TRUE;
ELSE
"DB2005_SD".SD[#for_i].Estado_Salida := FALSE;
END_IF;
END_CASE;
END_REGION ESCRITURA_SALIDA
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2005_SD".SD[#for_i].Orden_ActivarAuto := FALSE;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2005_SD".SD[#for_i].Estado_AutoMan AND NOT "DB2005_SD".SD[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_12_DISP_SD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SD",
CodInt_2 := #for_i,
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 "DB2005_SD".SD[#for_i].Estado_AutoMan AND "DB2005_SD".SD[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_12_DISP_SD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SD",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2005_SD".SD[#for_i].Estado_AutoMan AND "DB2005_SD".SD[#for_i].Estado_Activado AND NOT "DB2005_SD".SD[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_12_DISP_SD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SD",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2005_SD".SD[#for_i].Estado_AutoMan AND NOT "DB2005_SD".SD[#for_i].Estado_Activado AND "DB2005_SD".SD[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_12_DISP_SD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SD",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2005_SD".SD[#for_i].Aux_oldAutoMan := "DB2005_SD".SD[#for_i].Estado_AutoMan;
"DB2005_SD".SD[#for_i].Aux_oldActivado := "DB2005_SD".SD[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO DESHABILITADO
// =============================================================================
"DB2005_SD".SD[#for_i].Estado_Activado := FALSE;
"DB2005_SD".SD[#for_i].Estado_AutoMan := FALSE;
"DB2005_SD".SD[#for_i].Estado_Enclavado := FALSE;
"DB2005_SD".SD[#for_i].Estado_Salida := FALSE;
"DB2005_SD".SD[#for_i].Tiempos_TiempoAct := 0;
"DB2005_SD".SD[#for_i].Tiempos_TiempoDes := 0;
"DB2005_SD".SD[#for_i].Orden_ActivarManual := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2005_SD".SD[#for_i].Hmi_Estado.%X0 := "DB2005_SD".SD[#for_i].Config_Habilitar;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X1 := "DB2005_SD".SD[#for_i].Estado_AutoMan;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X2 := "DB2005_SD".SD[#for_i].Estado_Enclavado;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X3 := "DB2005_SD".SD[#for_i].Estado_Activado;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X8 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X13 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION
END_FOR;
END_REGION
END_NETWORK
END_FUNCTION
@@ -0,0 +1,19 @@
MultiLingualTexts:
- id: MLC_3pw
es-ES: FC2005_SD
- id: MLC_4ii
es-ES: Modelo de dispositivo salida digital
- id: MLC_36J
es-ES: Pulso de 1 segundo
- id: MLC_hm
es-ES: Simulacion
- id: MLC_4ik
es-ES: Primer arranque
- id: MLC_4ps
es-ES: Acuse general
- id: MLC_3d4
es-ES: Seguridades Ok
- id: MLC_4tF
es-ES: Usuario actual
- id: MLC_4ph
es-ES: Fecha y hora actual
@@ -1,411 +0,0 @@
FUNCTION "FC2005_ZC_SD" : Void
TITLE = FC2005_SD
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Modelo de dispositivo salida digital
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
Simulacion : Bool; // Simulacion
Arranque : Bool; // Primer arranque
Ack : Bool; // Acuse general
SeguridadesOK : Bool; // Seguridades Ok
UsuarioActual : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_ActivarConRetardo : Bool;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Salida Digital.
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 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 |
|----------|------------|---------|---------|
| 00.00.01 | 26.04.2022 | (HCR) | Primera version. Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. |
| 00.00.02 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.03 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones |
| 00.00.03 | 08.11.2022 | (HCR) | Se añade trazabilidad de activación manual |
| 00.00.04 | 20.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.05 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado Se añade movimiento de ED[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. |
| 00.00.08 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.09 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.10 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.11 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 00.00.12 | 14.05.2026 | (ABH) | Se modifica UDT para añadir tipo de lectura. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. Se corrigen logicas de activacion y se añade variable "Estado_Salida". |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2005_SD".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2005_SD".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2005_SD".NumDispositivos := "N_MAX_DISP_SD";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_SD" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_SD";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2005_SD".SD[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2005_SD".SD[#for_i].Config_GrupoAlarma < 0 THEN
"DB2005_SD".SD[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2005_SD".SD[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2005_SD".SD[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2005_SD".SD[#for_i].Estado_AutoMan THEN
"DB2005_SD".Agrup["DB2005_SD".SD[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2005_SD".SD[#for_i].Orden_ActivarManual AND #SeguridadesOK AND "DB2005_SD".SD[#for_i].Estado_SeguridadOk THEN // En caso de enclavar válvula en manual AND NOT "DB2005_SD".SD[#for_i].Estado_Enclavado
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
"DB2005_SD".SD[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2005_SD".SD[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2005_SD".SD[#for_i].Estado_AutoMan THEN
IF "DB2005_SD".SD[#for_i].Orden_ActivarAuto AND #SeguridadesOK AND "DB2005_SD".SD[#for_i].Estado_SeguridadOk AND NOT "DB2005_SD".SD[#for_i].Estado_Enclavado THEN
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
END_IF;
"DB2005_SD".SD[#for_i].Orden_ActivarManual := "DB2005_SD".SD[#for_i].Estado_Activado;
END_IF;
// Reset orden auto
"DB2005_SD".SD[#for_i].Orden_ActivarAuto := FALSE;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// SALIDA SIMULADA
IF #Simulacion THEN
IF #t_Activar THEN
"DB2005_SD".SD[#for_i].Estado_Activado := TRUE;
ELSE
"DB2005_SD".SD[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
IF NOT "DB2005_SD".SD[#for_i].Estado_AutoMan AND NOT #Simulacion THEN
// =============================================================================
// RETARDO A LA ACTIVACIÓN AUTOMÁTICO
IF #t_Activar AND "DB2005_SD".SD[#for_i].Config_HabilitarRetardoAct THEN
IF #Pulso1seg AND "DB2005_SD".SD[#for_i].Tiempos_TiempoAct < "DB2005_SD".SD[#for_i].Tiempos_SetPointTiempoAct THEN
"DB2005_SD".SD[#for_i].Tiempos_TiempoAct += 1;
END_IF;
IF "DB2005_SD".SD[#for_i].Tiempos_TiempoAct >= "DB2005_SD".SD[#for_i].Tiempos_SetPointTiempoAct THEN
#t_ActivarConRetardo := TRUE;
ELSE
#t_ActivarConRetardo := FALSE;
END_IF;
ELSIF #t_Activar THEN
#t_ActivarConRetardo := TRUE;
ELSE
"DB2005_SD".SD[#for_i].Tiempos_TiempoAct := 0;
END_IF;
// =============================================================================
// RETARDO A LA DESACTIVACIÓN AUTOMÁTICO
IF NOT #t_Activar AND "DB2005_SD".SD[#for_i].Config_HabilitarRetardoDes THEN
IF #Pulso1seg AND "DB2005_SD".SD[#for_i].Tiempos_TiempoDes < "DB2005_SD".SD[#for_i].Tiempos_SetPointTiempoDes THEN
"DB2005_SD".SD[#for_i].Tiempos_TiempoDes += 1;
END_IF;
IF "DB2005_SD".SD[#for_i].Tiempos_TiempoDes >= "DB2005_SD".SD[#for_i].Tiempos_SetPointTiempoDes THEN
#t_ActivarConRetardo := FALSE;
ELSE
#t_ActivarConRetardo := TRUE;
END_IF;
ELSIF NOT #t_Activar THEN
#t_ActivarConRetardo := FALSE;
ELSE
"DB2005_SD".SD[#for_i].Tiempos_TiempoDes := 0;
END_IF;
ELSE
#t_ActivarConRetardo := #t_Activar;
END_IF;
// Estado activado
"DB2005_SD".SD[#for_i].Estado_Activado := #t_ActivarConRetardo;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION ESCRITURA_SALIDA
// =============================================================================
// INVERSION DE LA SALIDA
IF "DB2005_SD".SD[#for_i].Config_HabilitarInvertir THEN
#t_ActivarConRetardo := NOT #t_ActivarConRetardo;
END_IF;
CASE "DB2005_SD".SD[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA VALORES DE LA TARJETA
IF "DB2005_SD".SD[#for_i].Config_DireccionSalidaByte >= 0 THEN
IF #t_ActivarConRetardo THEN
"DB2005_SD".SD[#for_i].Estado_Salida := TRUE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2005_SD".SD[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2005_SD".SD[#for_i].Config_DireccionSalidaBit,
value := TRUE);
ELSE
"DB2005_SD".SD[#for_i].Estado_Salida := FALSE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2005_SD".SD[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2005_SD".SD[#for_i].Config_DireccionSalidaBit,
value := FALSE);
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
IF #t_ActivarConRetardo THEN
"DB2005_SD".SD[#for_i].Estado_Salida := TRUE;
ELSE
"DB2005_SD".SD[#for_i].Estado_Salida := FALSE;
END_IF;
END_CASE;
END_REGION ESCRITURA_SALIDA
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2005_SD".SD[#for_i].Orden_ActivarAuto := FALSE;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2005_SD".SD[#for_i].Estado_AutoMan AND NOT "DB2005_SD".SD[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_12_DISP_SD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SD",
CodInt_2 := #for_i,
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 "DB2005_SD".SD[#for_i].Estado_AutoMan AND "DB2005_SD".SD[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_12_DISP_SD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SD",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2005_SD".SD[#for_i].Estado_AutoMan AND "DB2005_SD".SD[#for_i].Estado_Activado AND NOT "DB2005_SD".SD[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_12_DISP_SD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SD",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2005_SD".SD[#for_i].Estado_AutoMan AND NOT "DB2005_SD".SD[#for_i].Estado_Activado AND "DB2005_SD".SD[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_12_DISP_SD",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SD",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2005_SD".SD[#for_i].Aux_oldAutoMan := "DB2005_SD".SD[#for_i].Estado_AutoMan;
"DB2005_SD".SD[#for_i].Aux_oldActivado := "DB2005_SD".SD[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO DESHABILITADO
// =============================================================================
"DB2005_SD".SD[#for_i].Estado_Activado := FALSE;
"DB2005_SD".SD[#for_i].Estado_AutoMan := FALSE;
"DB2005_SD".SD[#for_i].Estado_Enclavado := FALSE;
"DB2005_SD".SD[#for_i].Estado_Salida := FALSE;
"DB2005_SD".SD[#for_i].Tiempos_TiempoAct := 0;
"DB2005_SD".SD[#for_i].Tiempos_TiempoDes := 0;
"DB2005_SD".SD[#for_i].Orden_ActivarManual := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2005_SD".SD[#for_i].Hmi_Estado.%X0 := "DB2005_SD".SD[#for_i].Config_Habilitar;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X1 := "DB2005_SD".SD[#for_i].Estado_AutoMan;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X2 := "DB2005_SD".SD[#for_i].Estado_Enclavado;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X3 := "DB2005_SD".SD[#for_i].Estado_Activado;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X8 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X13 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2005_SD".SD[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION
END_FOR;
END_REGION
END_FUNCTION
@@ -0,0 +1,483 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4mj";
S7_BlockNumber := "2006";
S7_BlockTitle := "MLC_fU";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2006_ZC_SA" : Void
VAR_INPUT
{ S7_MLC := "MLC_4bz" }
Ack : Bool;
{ S7_MLC := "MLC_3tt" }
SeguridadesOK : Bool;
{ S7_MLC := "MLC_593" }
UsuarioActual : String;
{ S7_MLC := "MLC_Ld" }
FechaHoraActual : DTL;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_X0 : Real;
t_X1 : Real;
t_Y0 : Real;
t_Y1 : Real;
X : Real;
t_ValorSalida : Real;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Salida Analogica.
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 la orden de escritura
- 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 |
|----------|------------|---------|---------|
| 00.00.00 | 15.10.2020 | (ABH) | Primera version. |
| 00.00.01 | 06.05.2021 | (ABH) | Añadido bit de enclavamiento. Cuando Estado_Enclavado este a TRUE, la salida tiene el valor de Config_ConsignaEnclavado. |
| 00.00.02 | 27.07.2021 | (ABH) | Se añade que si la direccion de salida es igual a 0, no realiza ningun calculo ni intenta escribir la salida para que no salten errores. Se añade variable aux para almacenar el mensaje de error, para que a la hora de generar las alarmas se generen adecuadamente. |
| 00.00.03 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. Se gestionan los BITs de visibilidad para Hmi. |
| 00.00.04 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.05 | 28.04.2022 | (HCR) | Se mantiene la salida al pasar a modo manual. |
| 00.00.06 | 29.04.2022 | (HCR) | Cambio del estado de la señal por prioridades. Cambio de las condiciones de para error de configuración, poniendo parámetros de ingenería y de tarjeta. Cambio en Alarmas. |
| 00.00.07 | 17.05.2022 | (BRM) | Cambio en escritura canal - Funcion POKE area: 16#82 para CPU 1200. |
| 00.00.08 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones. |
| 00.00.09 | 27.07.2023 | (ABH) | Se añade trazabilidad cambio parametros. |
| 00.00.10 | 18.09.2023 | (ABH) | Se añade reset de valor SP auto al final de la gestion del dispositivo. |
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado. Se añade movimiento de SA[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.12 | 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. |
| 00.00.13 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.14 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.15 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.16 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT.Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 09.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2006_SA".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2006_SA".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2006_SA".NumDispositivos := "N_MAX_DISP_SA";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_SA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_SA";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2006_SA".SA[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2006_SA".SA[#for_i].Config_GrupoAlarma < 0 THEN
"DB2006_SA".SA[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2006_SA".SA[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2006_SA".SA[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2006_SA".SA[#for_i].Estado_AutoMan THEN
"DB2006_SA".Agrup["DB2006_SA".SA[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
"DB2006_SA".SA[#for_i].Estado_ValorActual := "DB2006_SA".SA[#for_i].Orden_ConsignaManual;
ELSE
"DB2006_SA".SA[#for_i].Estado_ValorActual := "DB2006_SA".SA[#for_i].Orden_ConsignaAuto;
"DB2006_SA".SA[#for_i].Orden_ConsignaManual := "DB2006_SA".SA[#for_i].Orden_ConsignaAuto;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ERRORES
// =============================================================================
// LIMITE INFERIOR
IF "DB2006_SA".SA[#for_i].Estado_ValorActual <= "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria THEN
"DB2006_SA".SA[#for_i].Estado_ValorActual := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
END_IF;
// =============================================================================
// LIMITE SUPERIOR
IF "DB2006_SA".SA[#for_i].Estado_ValorActual >= "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria THEN
"DB2006_SA".SA[#for_i].Estado_ValorActual := "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria;
END_IF;
// =============================================================================
// ERROR MÍNIMO
IF "DB2006_SA".SA[#for_i].Config_HabilitarAlarmaMin AND
"DB2006_SA".SA[#for_i].Estado_ValorActual < "DB2006_SA".SA[#for_i].Config_ErrorMinIngenieria THEN
"DB2006_SA".SA[#for_i].Alarmas_Min := TRUE;
ELSE
"DB2006_SA".SA[#for_i].Alarmas_Min := FALSE;
END_IF;
// =============================================================================
// ERROR MÁXIMO
IF "DB2006_SA".SA[#for_i].Config_HabilitarAlarmaMax AND
"DB2006_SA".SA[#for_i].Estado_ValorActual > "DB2006_SA".SA[#for_i].Config_ErrorMaxIngenieria THEN
"DB2006_SA".SA[#for_i].Alarmas_Max := TRUE;
ELSE
"DB2006_SA".SA[#for_i].Alarmas_Max := FALSE;
END_IF;
END_REGION ERRORES
REGION SALIDA
IF NOT "DB2006_SA".SA[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2006_SA".SA[#for_i].Estado_SeguridadOk THEN
// =============================================================================
// CONVERSIÓN DE VALORES
#t_Y0 := INT_TO_REAL("DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta);
#t_Y1 := INT_TO_REAL("DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta);
#X := "DB2006_SA".SA[#for_i].Estado_ValorActual;
#t_X0 := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
#t_X1 := "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria;
// =============================================================================
// ESCALADO Y = (X-x0)*((y1-y0)/(x1-x0)) + y0
IF "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria > "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria AND
"DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta > "DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta THEN
#t_ValorSalida := ((#X - #t_X0) * ((#t_Y1 - #t_Y0) / (#t_X1 - #t_X0))) + #t_Y0;
"DB2006_SA".SA[#for_i].Alarmas_Parametros := FALSE;
#t_ValorSalida := #t_ValorSalida + "DB2006_SA".SA[#for_i].Config_EscaladoOffset;
ELSE
#t_ValorSalida := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
"DB2006_SA".SA[#for_i].Alarmas_Parametros := TRUE;
END_IF;
ELSE
// =============================================================================
// AJUSTE LIMITES CONSIGNA ENCLAVADO
IF "DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado > "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria THEN
"DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado := "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria;
END_IF;
IF "DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado < "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria THEN
"DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
END_IF;
// =============================================================================
// ESCRITURA SALIDA ENCLAVADA
// =============================================================================
// CONVERSIÓN DE VALORES
#t_Y0 := INT_TO_REAL("DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta);
#t_Y1 := INT_TO_REAL("DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta);
#X := "DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado;
#t_X0 := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
#t_X1 := "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria;
// =============================================================================
// ESCALADO Y = (X-x0)*((y1-y0)/(x1-x0)) + y0
IF "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria > "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria AND
"DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta > "DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta THEN
#t_ValorSalida := ((#X - #t_X0) * ((#t_Y1 - #t_Y0) / (#t_X1 - #t_X0))) + #t_Y0;
"DB2006_SA".SA[#for_i].Alarmas_Parametros := FALSE;
ELSE
#t_ValorSalida := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
"DB2006_SA".SA[#for_i].Alarmas_Parametros := TRUE;
END_IF;
END_IF;
REGION ESCRITURA_SALIDA
CASE "DB2006_SA".SA[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA DEL CANAL - POKE (area) 16#2 para CPU 1500 ; 16#82 para CPU 1200
IF "DB2006_SA".SA[#for_i].Config_DireccionSalidaByte > 0 THEN
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := REAL_TO_INT(#t_ValorSalida);
POKE(area := 16#82,
dbNumber := 0,
byteOffset := "DB2006_SA".SA[#for_i].Config_DireccionSalidaByte,
value := INT_TO_WORD("DB2006_SA".SA[#for_i].Estado_ValorTarjeta));
"DB2006_SA".SA[#for_i].Estado_Escritura := GET_ERR_ID();
IF "DB2006_SA".SA[#for_i].Estado_Escritura <> 0 THEN
"DB2006_SA".SA[#for_i].Alarmas_Escritura := TRUE;
ELSE
"DB2006_SA".SA[#for_i].Alarmas_Escritura := FALSE;
END_IF;
ELSE
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := 0;
END_IF;
#TIPO_ACCESO_PLC:
// =============================================================================
// ESCRITURA VALORES DE LA TARJETA
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := REAL_TO_INT(#t_ValorSalida);
END_CASE;
END_REGION ESCRITURA_SALIDA
END_REGION SALIDA
REGION ESTADO
// Señal normal
"DB2006_SA".SA[#for_i].Aux_Estado := 1;
// Límites alcanzados
IF "DB2006_SA".SA[#for_i].Estado_ValorActual < "DB2006_SA".SA[#for_i].Config_ErrorMinIngenieria AND
"DB2006_SA".SA[#for_i].Config_HabilitarAlarmaMin THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 4;
END_IF;
// Límites alcanzados
IF "DB2006_SA".SA[#for_i].Estado_ValorActual >= "DB2006_SA".SA[#for_i].Config_ErrorMaxIngenieria AND
"DB2006_SA".SA[#for_i].Config_HabilitarAlarmaMax THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 5;
END_IF;
// Señal forzada
IF "DB2006_SA".SA[#for_i].Estado_AutoMan THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 2;
END_IF;
// Enclavado
IF "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria > "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria AND
"DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta > "DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta AND
"DB2006_SA".SA[#for_i].Estado_Enclavado THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 9;
END_IF;
// Error parametros
IF "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria <= "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria OR
"DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta <= "DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 6;
END_IF;
// Error de escritura
IF "DB2006_SA".SA[#for_i].Estado_Escritura <> 0 THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 10;
END_IF;
// SA no direccionada
IF "DB2006_SA".SA[#for_i].Config_DireccionSalidaByte <= 0 THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 8;
END_IF;
END_REGION
"DB2006_SA".SA[#for_i].Alarmas_General := FALSE;
IF "DB2006_SA".SA[#for_i].Alarmas_Max OR
"DB2006_SA".SA[#for_i].Alarmas_Min OR
"DB2006_SA".SA[#for_i].Alarmas_Parametros OR
"DB2006_SA".SA[#for_i].Alarmas_Escritura
THEN
"DB2006_SA".Agrup["DB2006_SA".SA[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
"DB2006_SA".SA[#for_i].Alarmas_General := TRUE;
END_IF;
REGION TRAZABILIDAD
IF "DB2006_SA".SA[#for_i].Estado_AutoMan AND NOT "DB2006_SA".SA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_13_DISP_SA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SA",
CodInt_2 := #for_i,
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 "DB2006_SA".SA[#for_i].Estado_AutoMan AND "DB2006_SA".SA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_13_DISP_SA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2006_SA".SA[#for_i].Aux_oldAutoMan := "DB2006_SA".SA[#for_i].Estado_AutoMan;
// =============================================================================
// Reset de orden automatica
"DB2006_SA".SA[#for_i].Orden_ConsignaAuto := 0.0;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := 0;
"DB2006_SA".SA[#for_i].Hmi_Estado := 0; // Canal deshabilitado
"DB2006_SA".SA[#for_i].Estado_ValorActual := 0.0;
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := 0;
"DB2006_SA".SA[#for_i].Orden_ConsignaAuto := 0.0;
"DB2006_SA".SA[#for_i].Orden_ConsignaManual := 0.0;
"DB2006_SA".SA[#for_i].Estado_AutoMan := FALSE;
"DB2006_SA".SA[#for_i].Alarmas_General := FALSE;
"DB2006_SA".SA[#for_i].Alarmas_Max := FALSE;
"DB2006_SA".SA[#for_i].Alarmas_Min := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Reserva
// Bit 3 Reserva
// Bit 4 Enclavado
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma 5
// Bit 14 Reserva
// Bit 15 Reserva
"DB2006_SA".SA[#for_i].Hmi_Estado.%X0 := "DB2006_SA".SA[#for_i].Config_Habilitar;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X1 := "DB2006_SA".SA[#for_i].Estado_AutoMan;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X2 := "DB2006_SA".SA[#for_i].Estado_Enclavado;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X3 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X8 := "DB2006_SA".SA[#for_i].Alarmas_General;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X13 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2006_SA".SA[#for_i].Alarmas_General AND NOT "DB2006_SA".SA[#for_i].Aux_oldAlarma THEN
"DB2006_SA".Agrup["DB2006_SA".SA[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2006_SA".SA[#for_i].Aux_oldAlarma := "DB2006_SA".SA[#for_i].Alarmas_General;
END_REGION
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2006_SA".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION
END_NETWORK
END_FUNCTION
@@ -0,0 +1,13 @@
MultiLingualTexts:
- id: MLC_fU
es-ES: FC2006_SA
- id: MLC_4mj
es-ES: Modelo de dispositivo salida analogica
- id: MLC_4bz
es-ES: Acuse general
- id: MLC_3tt
es-ES: Seguridades Ok
- id: MLC_593
es-ES: Usuario actual
- id: MLC_Ld
es-ES: Fecha y hora actual
@@ -1,477 +0,0 @@
FUNCTION "FC2006_ZC_SA" : Void
TITLE = FC2006_SA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Modelo de dispositivo salida analogica
VAR_INPUT
Ack : Bool; // Acuse general
SeguridadesOK : Bool; // Seguridades Ok
UsuarioActual : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_X0 : Real;
t_X1 : Real;
t_Y0 : Real;
t_Y1 : Real;
X : Real;
t_ValorSalida : Real;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Salida Analogica.
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 la orden de escritura
- 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 |
|----------|------------|---------|---------|
| 00.00.00 | 15.10.2020 | (ABH) | Primera version. |
| 00.00.01 | 06.05.2021 | (ABH) | Añadido bit de enclavamiento. Cuando Estado_Enclavado este a TRUE, la salida tiene el valor de Config_ConsignaEnclavado. |
| 00.00.02 | 27.07.2021 | (ABH) | Se añade que si la direccion de salida es igual a 0, no realiza ningun calculo ni intenta escribir la salida para que no salten errores. Se añade variable aux para almacenar el mensaje de error, para que a la hora de generar las alarmas se generen adecuadamente. |
| 00.00.03 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. Se gestionan los BITs de visibilidad para Hmi. |
| 00.00.04 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.05 | 28.04.2022 | (HCR) | Se mantiene la salida al pasar a modo manual. |
| 00.00.06 | 29.04.2022 | (HCR) | Cambio del estado de la señal por prioridades. Cambio de las condiciones de para error de configuración, poniendo parámetros de ingenería y de tarjeta. Cambio en Alarmas. |
| 00.00.07 | 17.05.2022 | (BRM) | Cambio en escritura canal - Funcion POKE area: 16#82 para CPU 1200. |
| 00.00.08 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones. |
| 00.00.09 | 27.07.2023 | (ABH) | Se añade trazabilidad cambio parametros. |
| 00.00.10 | 18.09.2023 | (ABH) | Se añade reset de valor SP auto al final de la gestion del dispositivo. |
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado. Se añade movimiento de SA[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.12 | 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. |
| 00.00.13 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.14 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.15 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.16 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT.Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 09.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2006_SA".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2006_SA".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2006_SA".NumDispositivos := "N_MAX_DISP_SA";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_SA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_SA";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2006_SA".SA[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2006_SA".SA[#for_i].Config_GrupoAlarma < 0 THEN
"DB2006_SA".SA[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2006_SA".SA[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2006_SA".SA[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2006_SA".SA[#for_i].Estado_AutoMan THEN
"DB2006_SA".Agrup["DB2006_SA".SA[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
"DB2006_SA".SA[#for_i].Estado_ValorActual := "DB2006_SA".SA[#for_i].Orden_ConsignaManual;
ELSE
"DB2006_SA".SA[#for_i].Estado_ValorActual := "DB2006_SA".SA[#for_i].Orden_ConsignaAuto;
"DB2006_SA".SA[#for_i].Orden_ConsignaManual := "DB2006_SA".SA[#for_i].Orden_ConsignaAuto;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ERRORES
// =============================================================================
// LIMITE INFERIOR
IF "DB2006_SA".SA[#for_i].Estado_ValorActual <= "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria THEN
"DB2006_SA".SA[#for_i].Estado_ValorActual := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
END_IF;
// =============================================================================
// LIMITE SUPERIOR
IF "DB2006_SA".SA[#for_i].Estado_ValorActual >= "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria THEN
"DB2006_SA".SA[#for_i].Estado_ValorActual := "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria;
END_IF;
// =============================================================================
// ERROR MÍNIMO
IF "DB2006_SA".SA[#for_i].Config_HabilitarAlarmaMin AND
"DB2006_SA".SA[#for_i].Estado_ValorActual < "DB2006_SA".SA[#for_i].Config_ErrorMinIngenieria THEN
"DB2006_SA".SA[#for_i].Alarmas_Min := TRUE;
ELSE
"DB2006_SA".SA[#for_i].Alarmas_Min := FALSE;
END_IF;
// =============================================================================
// ERROR MÁXIMO
IF "DB2006_SA".SA[#for_i].Config_HabilitarAlarmaMax AND
"DB2006_SA".SA[#for_i].Estado_ValorActual > "DB2006_SA".SA[#for_i].Config_ErrorMaxIngenieria THEN
"DB2006_SA".SA[#for_i].Alarmas_Max := TRUE;
ELSE
"DB2006_SA".SA[#for_i].Alarmas_Max := FALSE;
END_IF;
END_REGION ERRORES
REGION SALIDA
IF NOT "DB2006_SA".SA[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2006_SA".SA[#for_i].Estado_SeguridadOk THEN
// =============================================================================
// CONVERSIÓN DE VALORES
#t_Y0 := INT_TO_REAL("DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta);
#t_Y1 := INT_TO_REAL("DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta);
#X := "DB2006_SA".SA[#for_i].Estado_ValorActual;
#t_X0 := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
#t_X1 := "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria;
// =============================================================================
// ESCALADO Y = (X-x0)*((y1-y0)/(x1-x0)) + y0
IF "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria > "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria AND
"DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta > "DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta THEN
#t_ValorSalida := ((#X - #t_X0) * ((#t_Y1 - #t_Y0) / (#t_X1 - #t_X0))) + #t_Y0;
"DB2006_SA".SA[#for_i].Alarmas_Parametros := FALSE;
#t_ValorSalida := #t_ValorSalida + "DB2006_SA".SA[#for_i].Config_EscaladoOffset;
ELSE
#t_ValorSalida := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
"DB2006_SA".SA[#for_i].Alarmas_Parametros := TRUE;
END_IF;
ELSE
// =============================================================================
// AJUSTE LIMITES CONSIGNA ENCLAVADO
IF "DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado > "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria THEN
"DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado := "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria;
END_IF;
IF "DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado < "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria THEN
"DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
END_IF;
// =============================================================================
// ESCRITURA SALIDA ENCLAVADA
// =============================================================================
// CONVERSIÓN DE VALORES
#t_Y0 := INT_TO_REAL("DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta);
#t_Y1 := INT_TO_REAL("DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta);
#X := "DB2006_SA".SA[#for_i].Orden_ConsignaEnclavado;
#t_X0 := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
#t_X1 := "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria;
// =============================================================================
// ESCALADO Y = (X-x0)*((y1-y0)/(x1-x0)) + y0
IF "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria > "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria AND
"DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta > "DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta THEN
#t_ValorSalida := ((#X - #t_X0) * ((#t_Y1 - #t_Y0) / (#t_X1 - #t_X0))) + #t_Y0;
"DB2006_SA".SA[#for_i].Alarmas_Parametros := FALSE;
ELSE
#t_ValorSalida := "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria;
"DB2006_SA".SA[#for_i].Alarmas_Parametros := TRUE;
END_IF;
END_IF;
REGION ESCRITURA_SALIDA
CASE "DB2006_SA".SA[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA DEL CANAL - POKE (area) 16#2 para CPU 1500 ; 16#82 para CPU 1200
IF "DB2006_SA".SA[#for_i].Config_DireccionSalidaByte > 0 THEN
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := REAL_TO_INT(#t_ValorSalida);
POKE(area := 16#82,
dbNumber := 0,
byteOffset := "DB2006_SA".SA[#for_i].Config_DireccionSalidaByte,
value := INT_TO_WORD("DB2006_SA".SA[#for_i].Estado_ValorTarjeta));
"DB2006_SA".SA[#for_i].Estado_Escritura := GET_ERR_ID();
IF "DB2006_SA".SA[#for_i].Estado_Escritura <> 0 THEN
"DB2006_SA".SA[#for_i].Alarmas_Escritura := TRUE;
ELSE
"DB2006_SA".SA[#for_i].Alarmas_Escritura := FALSE;
END_IF;
ELSE
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := 0;
END_IF;
#TIPO_ACCESO_PLC:
// =============================================================================
// ESCRITURA VALORES DE LA TARJETA
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := REAL_TO_INT(#t_ValorSalida);
END_CASE;
END_REGION ESCRITURA_SALIDA
END_REGION SALIDA
REGION ESTADO
// Señal normal
"DB2006_SA".SA[#for_i].Aux_Estado := 1;
// Límites alcanzados
IF "DB2006_SA".SA[#for_i].Estado_ValorActual < "DB2006_SA".SA[#for_i].Config_ErrorMinIngenieria AND
"DB2006_SA".SA[#for_i].Config_HabilitarAlarmaMin THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 4;
END_IF;
// Límites alcanzados
IF "DB2006_SA".SA[#for_i].Estado_ValorActual >= "DB2006_SA".SA[#for_i].Config_ErrorMaxIngenieria AND
"DB2006_SA".SA[#for_i].Config_HabilitarAlarmaMax THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 5;
END_IF;
// Señal forzada
IF "DB2006_SA".SA[#for_i].Estado_AutoMan THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 2;
END_IF;
// Enclavado
IF "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria > "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria AND
"DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta > "DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta AND
"DB2006_SA".SA[#for_i].Estado_Enclavado THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 9;
END_IF;
// Error parametros
IF "DB2006_SA".SA[#for_i].Config_EscaladoMaxIngenieria <= "DB2006_SA".SA[#for_i].Config_EscaladoMinIngenieria OR
"DB2006_SA".SA[#for_i].Config_EscaladoMaxTarjeta <= "DB2006_SA".SA[#for_i].Config_EscaladoMinTarjeta THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 6;
END_IF;
// Error de escritura
IF "DB2006_SA".SA[#for_i].Estado_Escritura <> 0 THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 10;
END_IF;
// SA no direccionada
IF "DB2006_SA".SA[#for_i].Config_DireccionSalidaByte <= 0 THEN
"DB2006_SA".SA[#for_i].Aux_Estado := 8;
END_IF;
END_REGION
"DB2006_SA".SA[#for_i].Alarmas_General := FALSE;
IF "DB2006_SA".SA[#for_i].Alarmas_Max OR
"DB2006_SA".SA[#for_i].Alarmas_Min OR
"DB2006_SA".SA[#for_i].Alarmas_Parametros OR
"DB2006_SA".SA[#for_i].Alarmas_Escritura
THEN
"DB2006_SA".Agrup["DB2006_SA".SA[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
"DB2006_SA".SA[#for_i].Alarmas_General := TRUE;
END_IF;
REGION TRAZABILIDAD
IF "DB2006_SA".SA[#for_i].Estado_AutoMan AND NOT "DB2006_SA".SA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_13_DISP_SA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SA",
CodInt_2 := #for_i,
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 "DB2006_SA".SA[#for_i].Estado_AutoMan AND "DB2006_SA".SA[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_13_DISP_SA",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_SA",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2006_SA".SA[#for_i].Aux_oldAutoMan := "DB2006_SA".SA[#for_i].Estado_AutoMan;
// =============================================================================
// Reset de orden automatica
"DB2006_SA".SA[#for_i].Orden_ConsignaAuto := 0.0;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := 0;
"DB2006_SA".SA[#for_i].Hmi_Estado := 0; // Canal deshabilitado
"DB2006_SA".SA[#for_i].Estado_ValorActual := 0.0;
"DB2006_SA".SA[#for_i].Estado_ValorTarjeta := 0;
"DB2006_SA".SA[#for_i].Orden_ConsignaAuto := 0.0;
"DB2006_SA".SA[#for_i].Orden_ConsignaManual := 0.0;
"DB2006_SA".SA[#for_i].Estado_AutoMan := FALSE;
"DB2006_SA".SA[#for_i].Alarmas_General := FALSE;
"DB2006_SA".SA[#for_i].Alarmas_Max := FALSE;
"DB2006_SA".SA[#for_i].Alarmas_Min := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Reserva
// Bit 3 Reserva
// Bit 4 Enclavado
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma 5
// Bit 14 Reserva
// Bit 15 Reserva
"DB2006_SA".SA[#for_i].Hmi_Estado.%X0 := "DB2006_SA".SA[#for_i].Config_Habilitar;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X1 := "DB2006_SA".SA[#for_i].Estado_AutoMan;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X2 := "DB2006_SA".SA[#for_i].Estado_Enclavado;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X3 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X8 := "DB2006_SA".SA[#for_i].Alarmas_General;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X13 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2006_SA".SA[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2006_SA".SA[#for_i].Alarmas_General AND NOT "DB2006_SA".SA[#for_i].Aux_oldAlarma THEN
"DB2006_SA".Agrup["DB2006_SA".SA[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2006_SA".SA[#for_i].Aux_oldAlarma := "DB2006_SA".SA[#for_i].Alarmas_General;
END_REGION
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2006_SA".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION
END_FUNCTION
@@ -0,0 +1,666 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_44X";
S7_BlockNumber := "2010";
S7_BlockTitle := "MLC_4Pn";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2010_ZC_V" : Void
VAR_INPUT
{ S7_MLC := "MLC_4wv" }
Pulso1seg : Bool;
{ S7_MLC := "MLC_3Ys" }
Simulacion : Bool;
{ S7_MLC := "MLC_4uK" }
Arranque : Bool;
{ S7_MLC := "MLC_r3" }
Ack : Bool;
{ S7_MLC := "MLC_3Ps" }
SeguridadesOK : Bool;
{ S7_MLC := "MLC_4UE" }
UsuarioActual : String;
{ S7_MLC := "MLC_4ub" }
FechaHoraActual : DTL;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_ErrorReposo : Bool;
t_ErrorTrabajo : Bool;
t_ErrorDoble : Bool;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Valvula.
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 numero de activaciones
- Se gestiona la orden de escritura
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | `DB1_SYS`, `DB2010_V` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2020 | (ABH) | Primera version. |
| 00.00.02 | 28.11.2020 | (ABH) | Añadidas alarmas mantenimiento |
| 00.00.03 | 28.07.2021 | (ABH) | Añadidas alarmas en doble posicion / ninguna posicion |
| 00.00.04 | 22.12.2021 | (ABH) | Añadido bit de visibilidad, a traves de variable CFG_Hmi_ |
| 00.00.05 | 26.04.2022 | (HCR) | Cambio UDT |
| 00.00.06 | 26.04.2022 | (HCR) | BITs de visibilidad para HMI, quitar enclavamiento para orden manual |
| 00.00.07 | 27.04.2022 | (HCR) | Si no esta habilitado se quita orden manual y se quita el estado manual. Se agrega Ack para alarma de confirmación de marcha. Se mantiene el estado de válvula cuando se pasa a manual. Cambio en lógica de errores. Cambio en la escritura de la salida |
| 00.00.08 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones |
| 00.00.09 | 08.11.2022 | (HCR) | Se añade trazabilidad de activación manual |
| 00.00.10 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento |
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado. Se añade movimiento de V[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.13 | 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. |
| 00.00.14 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.15 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.16 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.17 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 09.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2010_V".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2010_V".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2010_V".NumDispositivos := "N_MAX_DISP_V";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_V" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_V";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2010_V".V[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2010_V".V[#for_i].Config_GrupoAlarma < 0 THEN
"DB2010_V".V[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2010_V".V[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2010_V".V[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
// =============================================================================
// PUESTA A CERO DE LA SALIDA
#t_Activar := FALSE;
REGION LECTURA_VALORES
CASE "DB2010_V".V[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
(* AREA: Pueden seleccionarse las siguientes áreas: 16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#1: Entrada de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección en la que se lee. Solo se utilizan los 16 bits menos significativos.*)
// =============================================================================
// RETORNO REPOSO
IF "DB2010_V".V[#for_i].Config_DireccionRetReposoByte >= 0 THEN
"DB2010_V".V[#for_i].Estado_EntradaRetornoReposo := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionRetReposoByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionRetReposoBit);
IF "DB2010_V".V[#for_i].Config_HabilitarInvertirReposo THEN
"DB2010_V".V[#for_i].Estado_EntradaRetornoReposo := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionRetReposoByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionRetReposoBit));
END_IF;
END_IF;
// =============================================================================
// RETORNO TRABAJO
IF "DB2010_V".V[#for_i].Config_DireccionRetTrabajoByte >= 0 THEN
"DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionRetTrabajoByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionRetTrabajoBit);
IF "DB2010_V".V[#for_i].Config_HabilitarInvertirTrabajo THEN
"DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionRetTrabajoByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionRetTrabajoBit));
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
;
END_CASE;
END_REGION LECTURA_VALORES
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2010_V".V[#for_i].Estado_AutoMan THEN
"DB2010_V".Agrup["DB2010_V".V[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2010_V".V[#for_i].Orden_ActivarManual AND #SeguridadesOK AND "DB2010_V".V[#for_i].Estado_SeguridadOk THEN // En caso de enclavar válvula en manual AND NOT "DB2035_TMF1_DISP_V".V[#i].Estado_Enclavado
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
"DB2010_V".V[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2010_V".V[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2010_V".V[#for_i].Estado_AutoMan THEN
IF "DB2010_V".V[#for_i].Orden_ActivarAuto AND NOT "DB2010_V".V[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2010_V".V[#for_i].Estado_SeguridadOk THEN
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
END_IF;
"DB2010_V".V[#for_i].Orden_ActivarManual := "DB2010_V".V[#for_i].Estado_Activado;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// VÁLVULA SIMULADA
IF #Simulacion THEN
IF #t_Activar THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// VÁLVULA 2 RETORNOS (REPOSO + TRABAJO)
IF NOT #Simulacion AND
"DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo
THEN
IF "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo AND #t_Activar THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
END_IF;
IF "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo AND NOT #t_Activar THEN
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// VÁLVULA SOLO RETORNO REPOSO
IF NOT #Simulacion AND
"DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND NOT "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo
THEN
IF NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// VÁLVULA SOLO RETORNO TRABAJO
IF NOT #Simulacion AND
NOT "DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo
THEN
IF "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// VÁLVULA SIN RETORNOS
IF NOT #Simulacion AND
NOT "DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND NOT "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo
THEN
IF #t_Activar THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION ERRORES
// =============================================================================
// CHEQUEO RETORNO REPOSO
IF NOT #Simulacion AND
"DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND
(
(#t_Activar AND "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo)
OR
(NOT #t_Activar AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo)
)
THEN
#t_ErrorReposo := TRUE;
ELSE
#t_ErrorReposo := FALSE;
END_IF;
// =============================================================================
// CHEQUEO RETORNO TRABAJO
IF NOT #Simulacion AND
"DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo AND
(
(#t_Activar AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo)
OR
(NOT #t_Activar AND "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo)
)
THEN
#t_ErrorTrabajo := TRUE;
ELSE
#t_ErrorTrabajo := FALSE;
END_IF;
// =============================================================================
// CHEQUEO DOBLE RETORNO EN DOS POSICIONES O EN NINGUNA POSICION
IF NOT #Simulacion AND
("DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo)
AND
(
(NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo)
OR
("DB2010_V".V[#for_i].Estado_EntradaRetornoReposo AND "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo)
)
THEN
#t_ErrorDoble := TRUE;
ELSE
#t_ErrorDoble := FALSE;
END_IF;
// =============================================================================
// TEMPORIZADO DE ERRORES
IF (
("DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND #t_ErrorReposo AND NOT "DB2010_V".V[#for_i].Alarmas_Reposo)
OR
("DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo AND #t_ErrorTrabajo AND NOT "DB2010_V".V[#for_i].Alarmas_Trabajo)
OR
("DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo AND #t_ErrorDoble AND NOT "DB2010_V".V[#for_i].Alarmas_Doble)
)
THEN
IF #Pulso1seg AND NOT #Simulacion THEN
"DB2010_V".V[#for_i].Tiempos_Alarma += 1;
END_IF;
ELSE
"DB2010_V".V[#for_i].Tiempos_Alarma := 0;
END_IF;
// =============================================================================
// ERROR REPOSO
IF "DB2010_V".V[#for_i].Tiempos_Alarma >= "DB2010_V".V[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorReposo THEN
"DB2010_V".V[#for_i].Alarmas_Reposo := TRUE;
END_IF;
IF NOT #t_ErrorReposo THEN
"DB2010_V".V[#for_i].Alarmas_Reposo := FALSE;
END_IF;
// =============================================================================
// ERROR TRABAJO
IF "DB2010_V".V[#for_i].Tiempos_Alarma >= "DB2010_V".V[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorTrabajo THEN
"DB2010_V".V[#for_i].Alarmas_Trabajo := TRUE;
END_IF;
IF NOT #t_ErrorTrabajo THEN
"DB2010_V".V[#for_i].Alarmas_Trabajo := FALSE;
END_IF;
// =============================================================================
// ERROR DOBLE
IF "DB2010_V".V[#for_i].Tiempos_Alarma >= "DB2010_V".V[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorDoble THEN
"DB2010_V".V[#for_i].Alarmas_Doble := TRUE;
END_IF;
IF NOT #t_ErrorDoble THEN
"DB2010_V".V[#for_i].Alarmas_Doble := FALSE;
END_IF;
// =============================================================================
// ERROR GLOBAL
IF "DB2010_V".V[#for_i].Alarmas_Reposo OR
"DB2010_V".V[#for_i].Alarmas_Trabajo OR
"DB2010_V".V[#for_i].Alarmas_Doble
THEN
"DB2010_V".V[#for_i].Alarmas_General := TRUE;
END_IF;
IF #Ack AND
NOT "DB2010_V".V[#for_i].Alarmas_Reposo AND
NOT "DB2010_V".V[#for_i].Alarmas_Trabajo AND
NOT "DB2010_V".V[#for_i].Alarmas_Doble
THEN
"DB2010_V".V[#for_i].Alarmas_General := FALSE;
END_IF;
//"DB2010_V".V[#for_i].Alarmas_General := "DB2010_V".V[#for_i].Alarmas_Reposo OR "DB2010_V".V[#for_i].Alarmas_Trabajo OR "DB2010_V".V[#for_i].Alarmas_Doble;
IF "DB2010_V".V[#for_i].Alarmas_General THEN
"DB2010_V".Agrup["DB2010_V".V[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
END_IF;
END_REGION ERRORES
REGION DIAGNOSIS
// =============================================================================
// CONTADOR DE CICLOS DE ACTIVACIÓN DE LA VÁLVULA
IF ("DB2010_V".V[#for_i].Estado_Activado AND NOT "DB2010_V".V[#for_i].Aux_oldActivado)
OR
(NOT "DB2010_V".V[#for_i].Estado_Activado AND "DB2010_V".V[#for_i].Aux_oldActivado)
THEN
"DB2010_V".V[#for_i].Tiempos_Ciclos += 1;
END_IF;
IF "DB2010_V".V[#for_i].Orden_ResetCiclos THEN
"DB2010_V".V[#for_i].Tiempos_Ciclos := 0;
"DB2010_V".V[#for_i].Orden_ResetCiclos := FALSE;
END_IF;
// =============================================================================
// ALARMA CICLOS PARA MANTENIMIENTO ALCANZADOS
"DB2010_V".V[#for_i].Alarmas_Mantenimiento := FALSE;
IF "DB2010_V".V[#for_i].Config_HabilitarMantenimiento AND ("DB2010_V".V[#for_i].Tiempos_Ciclos > "DB2010_V".V[#for_i].Tiempos_SetPointCiclos) THEN
"DB2010_V".V[#for_i].Alarmas_Mantenimiento := TRUE;
END_IF;
END_REGION DIAGNOSIS
REGION ESCRITURA_SALIDA
CASE "DB2010_V".V[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA SALIDA
(* AREA: Pueden seleccionarse las siguientes áreas:16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#2: Salida de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección que se escribe, Solo se utilizan los 16 bits menos significativos.
BITOFFSET: Bit que se escribe,
VALUE: Valor que se escribe *)
IF "DB2010_V".V[#for_i].Config_DireccionSalidaByte >= 0 THEN
IF #t_Activar AND NOT "DB2010_V".V[#for_i].Alarmas_General THEN
"DB2010_V".V[#for_i].Estado_Salida := TRUE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionSalidaBit,
value := TRUE);
ELSE
"DB2010_V".V[#for_i].Estado_Salida := FALSE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionSalidaBit,
value := FALSE);
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
IF #t_Activar AND NOT "DB2010_V".V[#for_i].Alarmas_General THEN
"DB2010_V".V[#for_i].Estado_Salida := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Salida := FALSE;
END_IF;
END_CASE;
END_REGION
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2010_V".V[#for_i].Orden_ActivarAuto := FALSE;
"DB2010_V".V[#for_i].Estado_Enclavado := FALSE;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2010_V".V[#for_i].Estado_AutoMan AND NOT "DB2010_V".V[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_14_DISP_V",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_V",
CodInt_2 := #for_i,
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 "DB2010_V".V[#for_i].Estado_AutoMan AND "DB2010_V".V[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_14_DISP_V",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_V",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2010_V".V[#for_i].Estado_AutoMan AND "DB2010_V".V[#for_i].Estado_Activado AND NOT "DB2010_V".V[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_14_DISP_V",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_V",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2010_V".V[#for_i].Estado_AutoMan AND NOT "DB2010_V".V[#for_i].Estado_Activado AND "DB2010_V".V[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_14_DISP_V",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_V",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION TRAZABILIDAD_MANUALIZACION
// =============================================================================
// Estado anterior manual/automatico
"DB2010_V".V[#for_i].Aux_oldActivado := "DB2010_V".V[#for_i].Estado_Activado;
"DB2010_V".V[#for_i].Aux_oldAutoMan := "DB2010_V".V[#for_i].Estado_AutoMan;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2010_V".V[#for_i].Alarmas_General := FALSE;
"DB2010_V".V[#for_i].Alarmas_Reposo := FALSE;
"DB2010_V".V[#for_i].Alarmas_Trabajo := FALSE;
"DB2010_V".V[#for_i].Alarmas_Doble := FALSE;
"DB2010_V".V[#for_i].Alarmas_Mantenimiento := FALSE;
"DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo := FALSE;
"DB2010_V".V[#for_i].Estado_EntradaRetornoReposo := FALSE;
"DB2010_V".V[#for_i].Estado_Enclavado := FALSE;
"DB2010_V".V[#for_i].Estado_Salida := FALSE;
"DB2010_V".V[#for_i].Orden_ActivarAuto := FALSE;
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
"DB2010_V".V[#for_i].Tiempos_Alarma := 0;
"DB2010_V".V[#for_i].Orden_ActivarManual := FALSE;
"DB2010_V".V[#for_i].Estado_AutoMan := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2010_V".V[#for_i].Hmi_Estado.%X0 := "DB2010_V".V[#for_i].Config_Habilitar;
"DB2010_V".V[#for_i].Hmi_Estado.%X1 := "DB2010_V".V[#for_i].Estado_AutoMan;
"DB2010_V".V[#for_i].Hmi_Estado.%X2 := "DB2010_V".V[#for_i].Estado_Enclavado;
"DB2010_V".V[#for_i].Hmi_Estado.%X3 := "DB2010_V".V[#for_i].Estado_Activado;
"DB2010_V".V[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X8 := "DB2010_V".V[#for_i].Alarmas_General;
"DB2010_V".V[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X13 := "DB2010_V".V[#for_i].Alarmas_Mantenimiento;
"DB2010_V".V[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION GESTION_HMI
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2010_V".V[#for_i].Alarmas_General AND NOT "DB2010_V".V[#for_i].Aux_oldAlarma THEN
"DB2010_V".Agrup["DB2010_V".V[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2010_V".V[#for_i].Aux_oldAlarma := "DB2010_V".V[#for_i].Alarmas_General;
END_REGION GESTION_NUEVA_ALARMA
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2010_V".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION LOGICA_DEL_DISPOSITIVO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,19 @@
MultiLingualTexts:
- id: MLC_4Pn
es-ES: FC2010_V
- id: MLC_44X
es-ES: Modelo de dispositivo vlvula
- id: MLC_4wv
es-ES: Pulso de 1 segundo
- id: MLC_3Ys
es-ES: Simulacion
- id: MLC_4uK
es-ES: Primer arranque
- id: MLC_r3
es-ES: Acuse general
- id: MLC_3Ps
es-ES: Seguridades OK
- id: MLC_4UE
es-ES: Usuario actual
- id: MLC_4ub
es-ES: Fecha y hora actual
@@ -1,657 +0,0 @@
FUNCTION "FC2010_ZC_V" : Void
TITLE = FC2010_V
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Modelo de dispositivo vlvula
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
Simulacion : Bool; // Simulacion
Arranque : Bool; // Primer arranque
Ack : Bool; // Acuse general
SeguridadesOK : Bool; // Seguridades OK
UsuarioActual : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_ErrorReposo : Bool;
t_ErrorTrabajo : Bool;
t_ErrorDoble : Bool;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Valvula.
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 numero de activaciones
- Se gestiona la orden de escritura
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | `DB1_SYS`, `DB2010_V` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2020 | (ABH) | Primera version. |
| 00.00.02 | 28.11.2020 | (ABH) | Añadidas alarmas mantenimiento |
| 00.00.03 | 28.07.2021 | (ABH) | Añadidas alarmas en doble posicion / ninguna posicion |
| 00.00.04 | 22.12.2021 | (ABH) | Añadido bit de visibilidad, a traves de variable CFG_Hmi_ |
| 00.00.05 | 26.04.2022 | (HCR) | Cambio UDT |
| 00.00.06 | 26.04.2022 | (HCR) | BITs de visibilidad para HMI, quitar enclavamiento para orden manual |
| 00.00.07 | 27.04.2022 | (HCR) | Si no esta habilitado se quita orden manual y se quita el estado manual. Se agrega Ack para alarma de confirmación de marcha. Se mantiene el estado de válvula cuando se pasa a manual. Cambio en lógica de errores. Cambio en la escritura de la salida |
| 00.00.08 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones |
| 00.00.09 | 08.11.2022 | (HCR) | Se añade trazabilidad de activación manual |
| 00.00.10 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento |
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado. Se añade movimiento de V[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.13 | 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. |
| 00.00.14 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.15 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.16 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.17 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 09.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2010_V".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2010_V".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2010_V".NumDispositivos := "N_MAX_DISP_V";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_V" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_V";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2010_V".V[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2010_V".V[#for_i].Config_GrupoAlarma < 0 THEN
"DB2010_V".V[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2010_V".V[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2010_V".V[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
// =============================================================================
// PUESTA A CERO DE LA SALIDA
#t_Activar := FALSE;
REGION LECTURA_VALORES
CASE "DB2010_V".V[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
(* AREA: Pueden seleccionarse las siguientes áreas: 16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#1: Entrada de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección en la que se lee. Solo se utilizan los 16 bits menos significativos.*)
// =============================================================================
// RETORNO REPOSO
IF "DB2010_V".V[#for_i].Config_DireccionRetReposoByte >= 0 THEN
"DB2010_V".V[#for_i].Estado_EntradaRetornoReposo := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionRetReposoByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionRetReposoBit);
IF "DB2010_V".V[#for_i].Config_HabilitarInvertirReposo THEN
"DB2010_V".V[#for_i].Estado_EntradaRetornoReposo := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionRetReposoByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionRetReposoBit));
END_IF;
END_IF;
// =============================================================================
// RETORNO TRABAJO
IF "DB2010_V".V[#for_i].Config_DireccionRetTrabajoByte >= 0 THEN
"DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionRetTrabajoByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionRetTrabajoBit);
IF "DB2010_V".V[#for_i].Config_HabilitarInvertirTrabajo THEN
"DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionRetTrabajoByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionRetTrabajoBit));
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
;
END_CASE;
END_REGION LECTURA_VALORES
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2010_V".V[#for_i].Estado_AutoMan THEN
"DB2010_V".Agrup["DB2010_V".V[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2010_V".V[#for_i].Orden_ActivarManual AND #SeguridadesOK AND "DB2010_V".V[#for_i].Estado_SeguridadOk THEN // En caso de enclavar válvula en manual AND NOT "DB2035_TMF1_DISP_V".V[#i].Estado_Enclavado
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
"DB2010_V".V[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2010_V".V[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2010_V".V[#for_i].Estado_AutoMan THEN
IF "DB2010_V".V[#for_i].Orden_ActivarAuto AND NOT "DB2010_V".V[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2010_V".V[#for_i].Estado_SeguridadOk THEN
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
END_IF;
"DB2010_V".V[#for_i].Orden_ActivarManual := "DB2010_V".V[#for_i].Estado_Activado;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// VÁLVULA SIMULADA
IF #Simulacion THEN
IF #t_Activar THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// VÁLVULA 2 RETORNOS (REPOSO + TRABAJO)
IF NOT #Simulacion AND
"DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo
THEN
IF "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo AND #t_Activar THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
END_IF;
IF "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo AND NOT #t_Activar THEN
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// VÁLVULA SOLO RETORNO REPOSO
IF NOT #Simulacion AND
"DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND NOT "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo
THEN
IF NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// VÁLVULA SOLO RETORNO TRABAJO
IF NOT #Simulacion AND
NOT "DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo
THEN
IF "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// VÁLVULA SIN RETORNOS
IF NOT #Simulacion AND
NOT "DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND NOT "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo
THEN
IF #t_Activar THEN
"DB2010_V".V[#for_i].Estado_Activado := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION ERRORES
// =============================================================================
// CHEQUEO RETORNO REPOSO
IF NOT #Simulacion AND
"DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND
(
(#t_Activar AND "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo)
OR
(NOT #t_Activar AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo)
)
THEN
#t_ErrorReposo := TRUE;
ELSE
#t_ErrorReposo := FALSE;
END_IF;
// =============================================================================
// CHEQUEO RETORNO TRABAJO
IF NOT #Simulacion AND
"DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo AND
(
(#t_Activar AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo)
OR
(NOT #t_Activar AND "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo)
)
THEN
#t_ErrorTrabajo := TRUE;
ELSE
#t_ErrorTrabajo := FALSE;
END_IF;
// =============================================================================
// CHEQUEO DOBLE RETORNO EN DOS POSICIONES O EN NINGUNA POSICION
IF NOT #Simulacion AND
("DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo)
AND
(
(NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoReposo AND NOT "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo)
OR
("DB2010_V".V[#for_i].Estado_EntradaRetornoReposo AND "DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo)
)
THEN
#t_ErrorDoble := TRUE;
ELSE
#t_ErrorDoble := FALSE;
END_IF;
// =============================================================================
// TEMPORIZADO DE ERRORES
IF (
("DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND #t_ErrorReposo AND NOT "DB2010_V".V[#for_i].Alarmas_Reposo)
OR
("DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo AND #t_ErrorTrabajo AND NOT "DB2010_V".V[#for_i].Alarmas_Trabajo)
OR
("DB2010_V".V[#for_i].Config_HabilitarRetornoReposo AND "DB2010_V".V[#for_i].Config_HabilitarRetornoTrabajo AND #t_ErrorDoble AND NOT "DB2010_V".V[#for_i].Alarmas_Doble)
)
THEN
IF #Pulso1seg AND NOT #Simulacion THEN
"DB2010_V".V[#for_i].Tiempos_Alarma += 1;
END_IF;
ELSE
"DB2010_V".V[#for_i].Tiempos_Alarma := 0;
END_IF;
// =============================================================================
// ERROR REPOSO
IF "DB2010_V".V[#for_i].Tiempos_Alarma >= "DB2010_V".V[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorReposo THEN
"DB2010_V".V[#for_i].Alarmas_Reposo := TRUE;
END_IF;
IF NOT #t_ErrorReposo THEN
"DB2010_V".V[#for_i].Alarmas_Reposo := FALSE;
END_IF;
// =============================================================================
// ERROR TRABAJO
IF "DB2010_V".V[#for_i].Tiempos_Alarma >= "DB2010_V".V[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorTrabajo THEN
"DB2010_V".V[#for_i].Alarmas_Trabajo := TRUE;
END_IF;
IF NOT #t_ErrorTrabajo THEN
"DB2010_V".V[#for_i].Alarmas_Trabajo := FALSE;
END_IF;
// =============================================================================
// ERROR DOBLE
IF "DB2010_V".V[#for_i].Tiempos_Alarma >= "DB2010_V".V[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorDoble THEN
"DB2010_V".V[#for_i].Alarmas_Doble := TRUE;
END_IF;
IF NOT #t_ErrorDoble THEN
"DB2010_V".V[#for_i].Alarmas_Doble := FALSE;
END_IF;
// =============================================================================
// ERROR GLOBAL
IF "DB2010_V".V[#for_i].Alarmas_Reposo OR
"DB2010_V".V[#for_i].Alarmas_Trabajo OR
"DB2010_V".V[#for_i].Alarmas_Doble
THEN
"DB2010_V".V[#for_i].Alarmas_General := TRUE;
END_IF;
IF #Ack AND
NOT "DB2010_V".V[#for_i].Alarmas_Reposo AND
NOT "DB2010_V".V[#for_i].Alarmas_Trabajo AND
NOT "DB2010_V".V[#for_i].Alarmas_Doble
THEN
"DB2010_V".V[#for_i].Alarmas_General := FALSE;
END_IF;
//"DB2010_V".V[#for_i].Alarmas_General := "DB2010_V".V[#for_i].Alarmas_Reposo OR "DB2010_V".V[#for_i].Alarmas_Trabajo OR "DB2010_V".V[#for_i].Alarmas_Doble;
IF "DB2010_V".V[#for_i].Alarmas_General THEN
"DB2010_V".Agrup["DB2010_V".V[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
END_IF;
END_REGION ERRORES
REGION DIAGNOSIS
// =============================================================================
// CONTADOR DE CICLOS DE ACTIVACIÓN DE LA VÁLVULA
IF ("DB2010_V".V[#for_i].Estado_Activado AND NOT "DB2010_V".V[#for_i].Aux_oldActivado)
OR
(NOT "DB2010_V".V[#for_i].Estado_Activado AND "DB2010_V".V[#for_i].Aux_oldActivado)
THEN
"DB2010_V".V[#for_i].Tiempos_Ciclos += 1;
END_IF;
IF "DB2010_V".V[#for_i].Orden_ResetCiclos THEN
"DB2010_V".V[#for_i].Tiempos_Ciclos := 0;
"DB2010_V".V[#for_i].Orden_ResetCiclos := FALSE;
END_IF;
// =============================================================================
// ALARMA CICLOS PARA MANTENIMIENTO ALCANZADOS
"DB2010_V".V[#for_i].Alarmas_Mantenimiento := FALSE;
IF "DB2010_V".V[#for_i].Config_HabilitarMantenimiento AND ("DB2010_V".V[#for_i].Tiempos_Ciclos > "DB2010_V".V[#for_i].Tiempos_SetPointCiclos) THEN
"DB2010_V".V[#for_i].Alarmas_Mantenimiento := TRUE;
END_IF;
END_REGION DIAGNOSIS
REGION ESCRITURA_SALIDA
CASE "DB2010_V".V[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA SALIDA
(* AREA: Pueden seleccionarse las siguientes áreas:16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#2: Salida de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección que se escribe, Solo se utilizan los 16 bits menos significativos.
BITOFFSET: Bit que se escribe,
VALUE: Valor que se escribe *)
IF "DB2010_V".V[#for_i].Config_DireccionSalidaByte >= 0 THEN
IF #t_Activar AND NOT "DB2010_V".V[#for_i].Alarmas_General THEN
"DB2010_V".V[#for_i].Estado_Salida := TRUE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionSalidaBit,
value := TRUE);
ELSE
"DB2010_V".V[#for_i].Estado_Salida := FALSE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2010_V".V[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2010_V".V[#for_i].Config_DireccionSalidaBit,
value := FALSE);
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
IF #t_Activar AND NOT "DB2010_V".V[#for_i].Alarmas_General THEN
"DB2010_V".V[#for_i].Estado_Salida := TRUE;
ELSE
"DB2010_V".V[#for_i].Estado_Salida := FALSE;
END_IF;
END_CASE;
END_REGION
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2010_V".V[#for_i].Orden_ActivarAuto := FALSE;
"DB2010_V".V[#for_i].Estado_Enclavado := FALSE;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2010_V".V[#for_i].Estado_AutoMan AND NOT "DB2010_V".V[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_14_DISP_V",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_V",
CodInt_2 := #for_i,
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 "DB2010_V".V[#for_i].Estado_AutoMan AND "DB2010_V".V[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_14_DISP_V",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_V",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2010_V".V[#for_i].Estado_AutoMan AND "DB2010_V".V[#for_i].Estado_Activado AND NOT "DB2010_V".V[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_14_DISP_V",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_V",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2010_V".V[#for_i].Estado_AutoMan AND NOT "DB2010_V".V[#for_i].Estado_Activado AND "DB2010_V".V[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_14_DISP_V",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_V",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION TRAZABILIDAD_MANUALIZACION
// =============================================================================
// Estado anterior manual/automatico
"DB2010_V".V[#for_i].Aux_oldActivado := "DB2010_V".V[#for_i].Estado_Activado;
"DB2010_V".V[#for_i].Aux_oldAutoMan := "DB2010_V".V[#for_i].Estado_AutoMan;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2010_V".V[#for_i].Alarmas_General := FALSE;
"DB2010_V".V[#for_i].Alarmas_Reposo := FALSE;
"DB2010_V".V[#for_i].Alarmas_Trabajo := FALSE;
"DB2010_V".V[#for_i].Alarmas_Doble := FALSE;
"DB2010_V".V[#for_i].Alarmas_Mantenimiento := FALSE;
"DB2010_V".V[#for_i].Estado_EntradaRetornoTrabajo := FALSE;
"DB2010_V".V[#for_i].Estado_EntradaRetornoReposo := FALSE;
"DB2010_V".V[#for_i].Estado_Enclavado := FALSE;
"DB2010_V".V[#for_i].Estado_Salida := FALSE;
"DB2010_V".V[#for_i].Orden_ActivarAuto := FALSE;
"DB2010_V".V[#for_i].Estado_Activado := FALSE;
"DB2010_V".V[#for_i].Tiempos_Alarma := 0;
"DB2010_V".V[#for_i].Orden_ActivarManual := FALSE;
"DB2010_V".V[#for_i].Estado_AutoMan := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2010_V".V[#for_i].Hmi_Estado.%X0 := "DB2010_V".V[#for_i].Config_Habilitar;
"DB2010_V".V[#for_i].Hmi_Estado.%X1 := "DB2010_V".V[#for_i].Estado_AutoMan;
"DB2010_V".V[#for_i].Hmi_Estado.%X2 := "DB2010_V".V[#for_i].Estado_Enclavado;
"DB2010_V".V[#for_i].Hmi_Estado.%X3 := "DB2010_V".V[#for_i].Estado_Activado;
"DB2010_V".V[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X8 := "DB2010_V".V[#for_i].Alarmas_General;
"DB2010_V".V[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X13 := "DB2010_V".V[#for_i].Alarmas_Mantenimiento;
"DB2010_V".V[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2010_V".V[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION GESTION_HMI
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2010_V".V[#for_i].Alarmas_General AND NOT "DB2010_V".V[#for_i].Aux_oldAlarma THEN
"DB2010_V".Agrup["DB2010_V".V[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2010_V".V[#for_i].Aux_oldAlarma := "DB2010_V".V[#for_i].Alarmas_General;
END_REGION GESTION_NUEVA_ALARMA
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2010_V".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION LOGICA_DEL_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,602 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4ed";
S7_BlockNumber := "2015";
S7_BlockTitle := "MLC_3pg";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2015_ZC_M" : Void
VAR_INPUT
{ S7_MLC := "MLC_rZ" }
Pulso1seg : Bool;
{ S7_MLC := "MLC_446" }
Simulacion : Bool;
{ S7_MLC := "MLC_4fr" }
Ack : Bool;
{ S7_MLC := "MLC_NA" }
SeguridadesOK : Bool;
{ S7_MLC := "MLC_M4" }
UsuarioActual : String;
{ S7_MLC := "MLC_4ix" }
FechaHoraActual : DTL;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_ErrorTermico : Bool;
t_ErrorConfMarcha : Bool;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Motor arranque directo.
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 tiempo en marcha
- Se gestiona la orden de escritura
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO`, `FC15025_ZC_CONTADOR_TIEMPO` |
| FB | - |
| DB | `DB2015_DISP_M` |
| UDT | `ZC_DISP_M`, `ZC_DISP_MUX_M`, `ZC_CONTADOR_TIEMPO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2020 | (ABH) | Primera version.
| 00.00.02 | 28.11.2020 | (ABH) | Añadidas alarmas mantenimiento
| 00.00.03 | 26.10.2021 | (ABH) | Se integra la gestion de directo/inverso
| 00.00.04 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. Se quita el enclavamiento para orden manual.
| 00.00.05 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas.
| 00.00.06 | 27.04.2022 | (HCR) | Si no esta habilitado se quita orden manual y se quita el estado manual. Se agrega Ack para alarmas de confirmación de marcha. Se mantiene el estado cuando se pasa a manual. Cambio en lógica de errores. Cambio en la escritura de la salida. Cambio en laógica de modos
| 00.00.07 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones
| 00.00.08 | 08.11.2022 | (HCR) | Se añade trazabilidad de activación manual
| 00.00.09 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento
| 00.00.10 | 18.09.2023 | (ABH) | Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado)
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado Se añade movimiento de M[0] a multiplexado en caso de que el indice sea 0.
| 00.00.13 | 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.
| 00.00.14 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro.
| 00.00.15 | 24.04.2025 | (HCR) | Se añade gestion de idioma
| 00.00.16 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion.
| 00.00.17 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision.
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2015_M".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2015_M".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2015_M".NumDispositivos := "N_MAX_DISP_M";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_M" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_M";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2015_M".M[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2015_M".M[#for_i].Config_GrupoAlarma < 0 THEN
"DB2015_M".M[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2015_M".M[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2015_M".M[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
// =============================================================================
// PUESTA A CERO DE LA SALIDA
#t_Activar := FALSE;
REGION LECTURA_VALORES
CASE "DB2015_M".M[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
(* AREA: Pueden seleccionarse las siguientes áreas: 16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#1: Entrada de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección en la que se lee. Solo se utilizan los 16 bits menos significativos.*)
// =============================================================================
// RETORNO TERMICO
IF "DB2015_M".M[#for_i].Config_DireccionRetTermicoByte >= 0 THEN
"DB2015_M".M[#for_i].Estado_EntradaTermico := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionRetTermicoBit);
IF "DB2015_M".M[#for_i].Config_HabilitarInvertirTermico THEN
"DB2015_M".M[#for_i].Estado_EntradaTermico := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionRetTermicoBit));
END_IF;
END_IF;
// =============================================================================
// RETORNO CONFIRMACION DE MARCHA
IF "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaByte >= 0 THEN
"DB2015_M".M[#for_i].Estado_EntradaConfMarcha := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaBit);
IF "DB2015_M".M[#for_i].Config_HabilitarInvertirConfMarcha THEN
"DB2015_M".M[#for_i].Estado_EntradaConfMarcha := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaBit));
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
;
END_CASE;
END_REGION LECTURA_VALORES
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2015_M".M[#for_i].Estado_AutoMan THEN
"DB2015_M".Agrup["DB2015_M".M[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2015_M".M[#for_i].Orden_ActivarManual = TRUE AND #SeguridadesOK AND "DB2015_M".M[#for_i].Estado_SeguridadOk AND NOT "DB2015_M".M[#for_i].Alarmas_Termico THEN // En caso de enclavar manual NOT "DB2036_TMF1_DISP_M".M[#i].Estado_Enclavado
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
"DB2015_M".M[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2015_M".M[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2015_M".M[#for_i].Estado_AutoMan THEN
IF "DB2015_M".M[#for_i].Orden_ActivarAuto = TRUE AND NOT "DB2015_M".M[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2015_M".M[#for_i].Estado_SeguridadOk AND NOT "DB2015_M".M[#for_i].Alarmas_Termico THEN
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
END_IF;
"DB2015_M".M[#for_i].Orden_ActivarManual := "DB2015_M".M[#for_i].Estado_Activado;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// MOTOR SIMULADO
IF #Simulacion THEN
IF #t_Activar THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO TERMICO Y CONFIRMACION DE MARCHA
IF NOT #Simulacion AND "DB2015_M".M[#for_i].Config_HabilitarRetornoTermico
AND "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha
THEN
IF NOT "DB2015_M".M[#for_i].Estado_EntradaTermico AND "DB2015_M".M[#for_i].Estado_EntradaConfMarcha THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO SOLO TERMICO
IF NOT #Simulacion AND "DB2015_M".M[#for_i].Config_HabilitarRetornoTermico
AND NOT "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF NOT "DB2015_M".M[#for_i].Estado_EntradaTermico AND #t_Activar THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO SOLO CONFIRMACION DE MARCHA
IF NOT #Simulacion AND NOT "DB2015_M".M[#for_i].Config_HabilitarRetornoTermico
AND "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF "DB2015_M".M[#for_i].Estado_EntradaConfMarcha THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR SIN RETORNOS
IF NOT (#Simulacion) AND NOT ("DB2015_M".M[#for_i].Config_HabilitarRetornoTermico)
AND NOT ("DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha) THEN
IF #t_Activar THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION ERRORES
// =============================================================================
// CHEQUEO RETORNO TERMICO
IF NOT #Simulacion AND "DB2015_M".M[#for_i].Config_HabilitarRetornoTermico
AND "DB2015_M".M[#for_i].Estado_EntradaTermico THEN
#t_ErrorTermico := TRUE;
ELSE
#t_ErrorTermico := FALSE;
END_IF;
// =============================================================================
// CHEQUEO RETORNO CONFIRMACION DE MARCHA
IF NOT #Simulacion AND "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha AND
(
(#t_Activar AND NOT "DB2015_M".M[#for_i].Estado_EntradaConfMarcha)
OR
(NOT #t_Activar AND "DB2015_M".M[#for_i].Estado_EntradaConfMarcha)
) THEN
#t_ErrorConfMarcha := TRUE;
ELSE
#t_ErrorConfMarcha := FALSE;
END_IF;
// =============================================================================
// TEMPORIZADO DE ERRORES
IF "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha
AND #t_ErrorConfMarcha
AND NOT "DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha
AND NOT "DB2015_M".M[#for_i].Alarmas_Termico
THEN
IF NOT #Simulacion AND #Pulso1seg THEN
"DB2015_M".M[#for_i].Tiempos_Alarma += 1;
END_IF;
ELSE
"DB2015_M".M[#for_i].Tiempos_Alarma := 0;
END_IF;
// =============================================================================
// ERROR TERMICO
IF #t_ErrorTermico THEN
"DB2015_M".M[#for_i].Alarmas_Termico := TRUE;
END_IF;
IF NOT #t_ErrorTermico THEN
"DB2015_M".M[#for_i].Alarmas_Termico := FALSE;
END_IF;
// =============================================================================
// ERROR CONFIRMACION DE MARCHA
IF "DB2015_M".M[#for_i].Tiempos_Alarma >= "DB2015_M".M[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorConfMarcha THEN
"DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha := TRUE;
END_IF;
IF NOT #t_ErrorConfMarcha THEN
"DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha := FALSE;
END_IF;
// =============================================================================
// ERROR GLOBAL
IF "DB2015_M".M[#for_i].Alarmas_Termico OR
"DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha
THEN
"DB2015_M".M[#for_i].Alarmas_General := TRUE;
END_IF;
IF #Ack AND
NOT "DB2015_M".M[#for_i].Alarmas_Termico AND
NOT "DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha
THEN
"DB2015_M".M[#for_i].Alarmas_General := FALSE;
END_IF;
//"DB2015_M".M[#for_i].Alarmas_General := "DB2015_M".M[#for_i].Alarmas_Termico OR "DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha;
IF "DB2015_M".M[#for_i].Alarmas_General THEN
"DB2015_M".Agrup["DB2015_M".M[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
END_IF;
END_REGION ERRORES
REGION DIAGNOSIS
// =============================================================================
// CONTADOR DE HORAS DE FUNCIONAMIENTO
IF "DB2015_M".M[#for_i].Orden_ResetHoras THEN
"DB2015_M".M[#for_i].Tiempos_Mantenimiento := 0.0;
"DB2015_M".M[#for_i].Orden_ResetHoras := FALSE;
END_IF;
IF NOT #Simulacion AND #Pulso1seg AND "DB2015_M".M[#for_i].Estado_Activado THEN
"DB2015_M".M[#for_i].Tiempos_Mantenimiento += 0.00027778;
END_IF;
// =============================================================================
// ALARMA HORAS PARA MANTENIMIENTO ALCANZADOS
"DB2015_M".M[#for_i].Alarmas_Mantenimiento := FALSE;
IF "DB2015_M".M[#for_i].Config_HabilitarMantenimiento
AND
"DB2015_M".M[#for_i].Tiempos_Mantenimiento > "DB2015_M".M[#for_i].Tiempos_SetPointMantenimiento
THEN
"DB2015_M".M[#for_i].Alarmas_Mantenimiento := TRUE;
END_IF;
END_REGION DIAGNOSIS
REGION ESCRITURA_SALIDA
CASE "DB2015_M".M[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA SALIDA
(* AREA: Pueden seleccionarse las siguientes áreas:16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#2: Salida de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección que se escribe, Solo se utilizan los 16 bits menos significativos.
BITOFFSET: Bit que se escribe,
VALUE: Valor que se escribe *)
IF "DB2015_M".M[#for_i].Config_DireccionSalidaByte >= 0 THEN
IF #t_Activar AND NOT "DB2015_M".M[#for_i].Alarmas_General THEN
"DB2015_M".M[#for_i].Estado_Salida := TRUE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionSalidaBit,
value := TRUE);
ELSE
"DB2015_M".M[#for_i].Estado_Salida := FALSE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionSalidaBit,
value := FALSE);
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
IF #t_Activar AND NOT "DB2015_M".M[#for_i].Alarmas_General THEN
"DB2015_M".M[#for_i].Estado_Salida := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Salida := FALSE;
END_IF;
END_CASE;
END_REGION ESCRITURA_SALIDA
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2015_M".M[#for_i].Orden_ActivarAuto := FALSE;
"DB2015_M".M[#for_i].Estado_Enclavado := FALSE;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2015_M".M[#for_i].Estado_AutoMan AND NOT "DB2015_M".M[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_15_DISP_M",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M",
CodInt_2 := #for_i,
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 "DB2015_M".M[#for_i].Estado_AutoMan AND "DB2015_M".M[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_15_DISP_M",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2015_M".M[#for_i].Estado_AutoMan AND "DB2015_M".M[#for_i].Estado_Activado AND NOT "DB2015_M".M[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_15_DISP_M",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2015_M".M[#for_i].Estado_AutoMan AND NOT "DB2015_M".M[#for_i].Estado_Activado AND "DB2015_M".M[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_15_DISP_M",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION TRAZABILIDAD_MANUALIZACION
// =============================================================================
// Estado anterior manual/automatico
"DB2015_M".M[#for_i].Aux_oldAutoMan := "DB2015_M".M[#for_i].Estado_AutoMan;
"DB2015_M".M[#for_i].Aux_oldActivado := "DB2015_M".M[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2015_M".M[#for_i].Alarmas_General := FALSE;
"DB2015_M".M[#for_i].Alarmas_Termico := FALSE;
"DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha := FALSE;
"DB2015_M".M[#for_i].Alarmas_Mantenimiento := FALSE;
"DB2015_M".M[#for_i].Estado_EntradaTermico := FALSE;
"DB2015_M".M[#for_i].Estado_EntradaConfMarcha := FALSE;
"DB2015_M".M[#for_i].Estado_Enclavado := FALSE;
"DB2015_M".M[#for_i].Estado_Salida := FALSE;
"DB2015_M".M[#for_i].Orden_ActivarAuto := FALSE;
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
"DB2015_M".M[#for_i].Tiempos_Alarma := 0;
"DB2015_M".M[#for_i].Orden_ActivarManual := FALSE;
"DB2015_M".M[#for_i].Estado_AutoMan := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 alarmas General
// Bit 9 alarmas 1
// Bit 10 alarmas 2
// Bit 11 alarmas 3
// Bit 12 alarmas 4
// Bit 13 alarmas Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2015_M".M[#for_i].Hmi_Estado.%X0 := "DB2015_M".M[#for_i].Config_Habilitar;
"DB2015_M".M[#for_i].Hmi_Estado.%X1 := "DB2015_M".M[#for_i].Estado_AutoMan;
"DB2015_M".M[#for_i].Hmi_Estado.%X2 := "DB2015_M".M[#for_i].Estado_Enclavado;
"DB2015_M".M[#for_i].Hmi_Estado.%X3 := "DB2015_M".M[#for_i].Estado_Activado;
"DB2015_M".M[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X8 := "DB2015_M".M[#for_i].Alarmas_General;
"DB2015_M".M[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X13 := "DB2015_M".M[#for_i].Alarmas_Mantenimiento;
"DB2015_M".M[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION GESTION_HMI
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA alarmas
IF "DB2015_M".M[#for_i].Alarmas_General AND NOT "DB2015_M".M[#for_i].Aux_oldAlarma THEN
"DB2015_M".Agrup["DB2015_M".M[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmass
"DB2015_M".M[#for_i].Aux_oldAlarma := "DB2015_M".M[#for_i].Alarmas_General;
END_REGION GESTION_NUEVA_ALARMA
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2015_M".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION LOGICA_DEL_DISPOSITIVO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,17 @@
MultiLingualTexts:
- id: MLC_3pg
es-ES: FC2015_M
- id: MLC_4ed
es-ES: Modelo de dispositivo motor
- id: MLC_rZ
es-ES: Pulso de 1 segundo
- id: MLC_446
es-ES: Simulacion
- id: MLC_4fr
es-ES: Acuse general
- id: MLC_NA
es-ES: Seguridades OK
- id: MLC_M4
es-ES: Usuario actual
- id: MLC_4ix
es-ES: Fecha y hora actual
@@ -1,594 +0,0 @@
FUNCTION "FC2015_ZC_M" : Void
TITLE = FC2015_M
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Modelo de dispositivo motor
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
Simulacion : Bool; // Simulacion
Ack : Bool; // Acuse general
SeguridadesOK : Bool; // Seguridades OK
UsuarioActual : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_ErrorTermico : Bool;
t_ErrorConfMarcha : Bool;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Motor arranque directo.
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 tiempo en marcha
- Se gestiona la orden de escritura
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO`, `FC15025_ZC_CONTADOR_TIEMPO` |
| FB | - |
| DB | `DB2015_DISP_M` |
| UDT | `ZC_DISP_M`, `ZC_DISP_MUX_M`, `ZC_CONTADOR_TIEMPO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2020 | (ABH) | Primera version.
| 00.00.02 | 28.11.2020 | (ABH) | Añadidas alarmas mantenimiento
| 00.00.03 | 26.10.2021 | (ABH) | Se integra la gestion de directo/inverso
| 00.00.04 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. Se quita el enclavamiento para orden manual.
| 00.00.05 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas.
| 00.00.06 | 27.04.2022 | (HCR) | Si no esta habilitado se quita orden manual y se quita el estado manual. Se agrega Ack para alarmas de confirmación de marcha. Se mantiene el estado cuando se pasa a manual. Cambio en lógica de errores. Cambio en la escritura de la salida. Cambio en laógica de modos
| 00.00.07 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones
| 00.00.08 | 08.11.2022 | (HCR) | Se añade trazabilidad de activación manual
| 00.00.09 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento
| 00.00.10 | 18.09.2023 | (ABH) | Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado)
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado Se añade movimiento de M[0] a multiplexado en caso de que el indice sea 0.
| 00.00.13 | 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.
| 00.00.14 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro.
| 00.00.15 | 24.04.2025 | (HCR) | Se añade gestion de idioma
| 00.00.16 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion.
| 00.00.17 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision.
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2015_M".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2015_M".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2015_M".NumDispositivos := "N_MAX_DISP_M";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_M" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_M";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2015_M".M[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2015_M".M[#for_i].Config_GrupoAlarma < 0 THEN
"DB2015_M".M[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2015_M".M[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2015_M".M[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
// =============================================================================
// PUESTA A CERO DE LA SALIDA
#t_Activar := FALSE;
REGION LECTURA_VALORES
CASE "DB2015_M".M[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
(* AREA: Pueden seleccionarse las siguientes áreas: 16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#1: Entrada de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección en la que se lee. Solo se utilizan los 16 bits menos significativos.*)
// =============================================================================
// RETORNO TERMICO
IF "DB2015_M".M[#for_i].Config_DireccionRetTermicoByte >= 0 THEN
"DB2015_M".M[#for_i].Estado_EntradaTermico := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionRetTermicoBit);
IF "DB2015_M".M[#for_i].Config_HabilitarInvertirTermico THEN
"DB2015_M".M[#for_i].Estado_EntradaTermico := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionRetTermicoBit));
END_IF;
END_IF;
// =============================================================================
// RETORNO CONFIRMACION DE MARCHA
IF "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaByte >= 0 THEN
"DB2015_M".M[#for_i].Estado_EntradaConfMarcha := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaBit);
IF "DB2015_M".M[#for_i].Config_HabilitarInvertirConfMarcha THEN
"DB2015_M".M[#for_i].Estado_EntradaConfMarcha := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionRetConfMarchaBit));
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
;
END_CASE;
END_REGION LECTURA_VALORES
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2015_M".M[#for_i].Estado_AutoMan THEN
"DB2015_M".Agrup["DB2015_M".M[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2015_M".M[#for_i].Orden_ActivarManual = TRUE AND #SeguridadesOK AND "DB2015_M".M[#for_i].Estado_SeguridadOk AND NOT "DB2015_M".M[#for_i].Alarmas_Termico THEN // En caso de enclavar manual NOT "DB2036_TMF1_DISP_M".M[#i].Estado_Enclavado
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
"DB2015_M".M[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2015_M".M[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2015_M".M[#for_i].Estado_AutoMan THEN
IF "DB2015_M".M[#for_i].Orden_ActivarAuto = TRUE AND NOT "DB2015_M".M[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2015_M".M[#for_i].Estado_SeguridadOk AND NOT "DB2015_M".M[#for_i].Alarmas_Termico THEN
#t_Activar := TRUE;
ELSE
#t_Activar := FALSE;
END_IF;
"DB2015_M".M[#for_i].Orden_ActivarManual := "DB2015_M".M[#for_i].Estado_Activado;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// MOTOR SIMULADO
IF #Simulacion THEN
IF #t_Activar THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO TERMICO Y CONFIRMACION DE MARCHA
IF NOT #Simulacion AND "DB2015_M".M[#for_i].Config_HabilitarRetornoTermico
AND "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha
THEN
IF NOT "DB2015_M".M[#for_i].Estado_EntradaTermico AND "DB2015_M".M[#for_i].Estado_EntradaConfMarcha THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO SOLO TERMICO
IF NOT #Simulacion AND "DB2015_M".M[#for_i].Config_HabilitarRetornoTermico
AND NOT "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF NOT "DB2015_M".M[#for_i].Estado_EntradaTermico AND #t_Activar THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO SOLO CONFIRMACION DE MARCHA
IF NOT #Simulacion AND NOT "DB2015_M".M[#for_i].Config_HabilitarRetornoTermico
AND "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF "DB2015_M".M[#for_i].Estado_EntradaConfMarcha THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR SIN RETORNOS
IF NOT (#Simulacion) AND NOT ("DB2015_M".M[#for_i].Config_HabilitarRetornoTermico)
AND NOT ("DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha) THEN
IF #t_Activar THEN
"DB2015_M".M[#for_i].Estado_Activado := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION ERRORES
// =============================================================================
// CHEQUEO RETORNO TERMICO
IF NOT #Simulacion AND "DB2015_M".M[#for_i].Config_HabilitarRetornoTermico
AND "DB2015_M".M[#for_i].Estado_EntradaTermico THEN
#t_ErrorTermico := TRUE;
ELSE
#t_ErrorTermico := FALSE;
END_IF;
// =============================================================================
// CHEQUEO RETORNO CONFIRMACION DE MARCHA
IF NOT #Simulacion AND "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha AND
(
(#t_Activar AND NOT "DB2015_M".M[#for_i].Estado_EntradaConfMarcha)
OR
(NOT #t_Activar AND "DB2015_M".M[#for_i].Estado_EntradaConfMarcha)
) THEN
#t_ErrorConfMarcha := TRUE;
ELSE
#t_ErrorConfMarcha := FALSE;
END_IF;
// =============================================================================
// TEMPORIZADO DE ERRORES
IF "DB2015_M".M[#for_i].Config_HabilitarRetornoConfMarcha
AND #t_ErrorConfMarcha
AND NOT "DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha
AND NOT "DB2015_M".M[#for_i].Alarmas_Termico
THEN
IF NOT #Simulacion AND #Pulso1seg THEN
"DB2015_M".M[#for_i].Tiempos_Alarma += 1;
END_IF;
ELSE
"DB2015_M".M[#for_i].Tiempos_Alarma := 0;
END_IF;
// =============================================================================
// ERROR TERMICO
IF #t_ErrorTermico THEN
"DB2015_M".M[#for_i].Alarmas_Termico := TRUE;
END_IF;
IF NOT #t_ErrorTermico THEN
"DB2015_M".M[#for_i].Alarmas_Termico := FALSE;
END_IF;
// =============================================================================
// ERROR CONFIRMACION DE MARCHA
IF "DB2015_M".M[#for_i].Tiempos_Alarma >= "DB2015_M".M[#for_i].Tiempos_SetPointTiempoAlarma AND #t_ErrorConfMarcha THEN
"DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha := TRUE;
END_IF;
IF NOT #t_ErrorConfMarcha THEN
"DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha := FALSE;
END_IF;
// =============================================================================
// ERROR GLOBAL
IF "DB2015_M".M[#for_i].Alarmas_Termico OR
"DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha
THEN
"DB2015_M".M[#for_i].Alarmas_General := TRUE;
END_IF;
IF #Ack AND
NOT "DB2015_M".M[#for_i].Alarmas_Termico AND
NOT "DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha
THEN
"DB2015_M".M[#for_i].Alarmas_General := FALSE;
END_IF;
//"DB2015_M".M[#for_i].Alarmas_General := "DB2015_M".M[#for_i].Alarmas_Termico OR "DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha;
IF "DB2015_M".M[#for_i].Alarmas_General THEN
"DB2015_M".Agrup["DB2015_M".M[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
END_IF;
END_REGION ERRORES
REGION DIAGNOSIS
// =============================================================================
// CONTADOR DE HORAS DE FUNCIONAMIENTO
IF "DB2015_M".M[#for_i].Orden_ResetHoras THEN
"DB2015_M".M[#for_i].Tiempos_Mantenimiento := 0.0;
"DB2015_M".M[#for_i].Orden_ResetHoras := FALSE;
END_IF;
IF NOT #Simulacion AND #Pulso1seg AND "DB2015_M".M[#for_i].Estado_Activado THEN
"DB2015_M".M[#for_i].Tiempos_Mantenimiento += 0.00027778;
END_IF;
// =============================================================================
// ALARMA HORAS PARA MANTENIMIENTO ALCANZADOS
"DB2015_M".M[#for_i].Alarmas_Mantenimiento := FALSE;
IF "DB2015_M".M[#for_i].Config_HabilitarMantenimiento
AND
"DB2015_M".M[#for_i].Tiempos_Mantenimiento > "DB2015_M".M[#for_i].Tiempos_SetPointMantenimiento
THEN
"DB2015_M".M[#for_i].Alarmas_Mantenimiento := TRUE;
END_IF;
END_REGION DIAGNOSIS
REGION ESCRITURA_SALIDA
CASE "DB2015_M".M[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA SALIDA
(* AREA: Pueden seleccionarse las siguientes áreas:16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#2: Salida de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección que se escribe, Solo se utilizan los 16 bits menos significativos.
BITOFFSET: Bit que se escribe,
VALUE: Valor que se escribe *)
IF "DB2015_M".M[#for_i].Config_DireccionSalidaByte >= 0 THEN
IF #t_Activar AND NOT "DB2015_M".M[#for_i].Alarmas_General THEN
"DB2015_M".M[#for_i].Estado_Salida := TRUE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionSalidaBit,
value := TRUE);
ELSE
"DB2015_M".M[#for_i].Estado_Salida := FALSE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2015_M".M[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2015_M".M[#for_i].Config_DireccionSalidaBit,
value := FALSE);
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
IF #t_Activar AND NOT "DB2015_M".M[#for_i].Alarmas_General THEN
"DB2015_M".M[#for_i].Estado_Salida := TRUE;
ELSE
"DB2015_M".M[#for_i].Estado_Salida := FALSE;
END_IF;
END_CASE;
END_REGION ESCRITURA_SALIDA
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2015_M".M[#for_i].Orden_ActivarAuto := FALSE;
"DB2015_M".M[#for_i].Estado_Enclavado := FALSE;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2015_M".M[#for_i].Estado_AutoMan AND NOT "DB2015_M".M[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_15_DISP_M",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M",
CodInt_2 := #for_i,
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 "DB2015_M".M[#for_i].Estado_AutoMan AND "DB2015_M".M[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_15_DISP_M",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2015_M".M[#for_i].Estado_AutoMan AND "DB2015_M".M[#for_i].Estado_Activado AND NOT "DB2015_M".M[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_15_DISP_M",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2015_M".M[#for_i].Estado_AutoMan AND NOT "DB2015_M".M[#for_i].Estado_Activado AND "DB2015_M".M[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_15_DISP_M",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION TRAZABILIDAD_MANUALIZACION
// =============================================================================
// Estado anterior manual/automatico
"DB2015_M".M[#for_i].Aux_oldAutoMan := "DB2015_M".M[#for_i].Estado_AutoMan;
"DB2015_M".M[#for_i].Aux_oldActivado := "DB2015_M".M[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2015_M".M[#for_i].Alarmas_General := FALSE;
"DB2015_M".M[#for_i].Alarmas_Termico := FALSE;
"DB2015_M".M[#for_i].Alarmas_ConfirmacionMarcha := FALSE;
"DB2015_M".M[#for_i].Alarmas_Mantenimiento := FALSE;
"DB2015_M".M[#for_i].Estado_EntradaTermico := FALSE;
"DB2015_M".M[#for_i].Estado_EntradaConfMarcha := FALSE;
"DB2015_M".M[#for_i].Estado_Enclavado := FALSE;
"DB2015_M".M[#for_i].Estado_Salida := FALSE;
"DB2015_M".M[#for_i].Orden_ActivarAuto := FALSE;
"DB2015_M".M[#for_i].Estado_Activado := FALSE;
"DB2015_M".M[#for_i].Tiempos_Alarma := 0;
"DB2015_M".M[#for_i].Orden_ActivarManual := FALSE;
"DB2015_M".M[#for_i].Estado_AutoMan := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 alarmas General
// Bit 9 alarmas 1
// Bit 10 alarmas 2
// Bit 11 alarmas 3
// Bit 12 alarmas 4
// Bit 13 alarmas Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2015_M".M[#for_i].Hmi_Estado.%X0 := "DB2015_M".M[#for_i].Config_Habilitar;
"DB2015_M".M[#for_i].Hmi_Estado.%X1 := "DB2015_M".M[#for_i].Estado_AutoMan;
"DB2015_M".M[#for_i].Hmi_Estado.%X2 := "DB2015_M".M[#for_i].Estado_Enclavado;
"DB2015_M".M[#for_i].Hmi_Estado.%X3 := "DB2015_M".M[#for_i].Estado_Activado;
"DB2015_M".M[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X8 := "DB2015_M".M[#for_i].Alarmas_General;
"DB2015_M".M[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X13 := "DB2015_M".M[#for_i].Alarmas_Mantenimiento;
"DB2015_M".M[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2015_M".M[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION GESTION_HMI
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA alarmas
IF "DB2015_M".M[#for_i].Alarmas_General AND NOT "DB2015_M".M[#for_i].Aux_oldAlarma THEN
"DB2015_M".Agrup["DB2015_M".M[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmass
"DB2015_M".M[#for_i].Aux_oldAlarma := "DB2015_M".M[#for_i].Alarmas_General;
END_REGION GESTION_NUEVA_ALARMA
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2015_M".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION LOGICA_DEL_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,703 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4yh";
S7_BlockNumber := "2016";
S7_BlockTitle := "MLC_4Pg";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2016_ZC_M_VF" : Void
VAR_INPUT
{ S7_MLC := "MLC_4G3" }
Pulso1seg : Bool;
{ S7_MLC := "MLC_3hd" }
Simulacion : Bool;
{ S7_MLC := "MLC_cH" }
Arranque : Bool;
{ S7_MLC := "MLC_33e" }
Ack : Bool;
{ S7_MLC := "MLC_3GW" }
SeguridadesOK : Bool;
{ S7_MLC := "MLC_39w" }
UsuarioActual : String;
{ S7_MLC := "MLC_Nh" }
FechaHoraActual : DTL;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_ErrorTermico : Bool;
t_ErrorConfirmacionMarcha : Bool;
t_Frecuencia : Real;
t_x : Real;
t_x0 : Real;
t_x1 : Real;
t_y0 : Real;
t_y1 : Real;
t_ValorSalida : Real;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Motor con variador de frecuencia.
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 tiempo en marcha
- Se gestiona la orden de escritura
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO`, `FC15025_ZC_CONTADOR_TIEMPO` |
| FB | - |
| DB | DB2016_DISP_M_VF |
| UDT | ZC_DISP_M_VF, ZC_DISP_MUX_M_VF, `ZC_CONTADOR_TIEMPO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2020 | (ABH) | Primera version. |
| 00.00.02 | 28.11.2020 | (ABH) | Añadidas alarmas mantenimiento |
| 00.00.03 | 26.10.2021 | (ABH) | Se integra la gestion de directo/inverso |
| 00.00.04 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. Se quita el enclavamiento para orden manual. |
| 00.00.05 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.06 | 27.04.2022 | (HCR) | Si no esta habilitado se quita orden manual y se quita el estado manual. Se agrega Ack para alarmas de confirmación de marcha. Se mantiene el estado cuando se pasa a manual. Cambio en lógica de errores. Cambio en la escritura de la salida. Cambio en laógica de modos |
| 00.00.07 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones |
| 00.00.08 | 08.11.2022 | (HCR) | Se añade trazabilidad de activación manual |
| 00.00.09 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento |
| 00.00.10 | 18.09.2023 | (ABH) | Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO |
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado Se añade movimiento de M[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.13 | 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. |
| 00.00.14 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.15 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.16 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.17 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2016_M_VF".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2016_M_VF".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2016_M_VF".NumDispositivos := "N_MAX_DISP_M_VF";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_M_VF" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_M_VF";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2016_M_VF".M_VF[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma < 0 THEN
"DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
// =============================================================================
// PUESTA A CERO DE LA SALIDA
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
REGION LECTURA_VALORES
CASE "DB2016_M_VF".M_VF[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
(* AREA: Pueden seleccionarse las siguientes áreas: 16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#1: Entrada de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección en la que se lee. Solo se utilizan los 16 bits menos significativos.*)
IF "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoByte >= 0 THEN
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoBit);
IF "DB2016_M_VF".M_VF[#for_i].Config_HabilitarInvertirTermico THEN
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoBit));
END_IF;
END_IF;
// =============================================================================
// RETORNO CONFIRMACION DE MARCHA
IF "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaByte >= 0 THEN
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaBit);
IF "DB2016_M_VF".M_VF[#for_i].Config_HabilitarInvertirConfMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaBit));
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
;
END_CASE;
END_REGION LECTURA_VALORES
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan THEN
"DB2016_M_VF".Agrup["DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual = TRUE AND #SeguridadesOK AND "DB2016_M_VF".M_VF[#for_i].Estado_SeguridadOk AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico THEN //En caso de enclavar manual NOT "DB2037_TMF1_DISP_M_VF".M_VF[#i].Estado_Enclavado
#t_Activar := TRUE;
#t_Frecuencia := "DB2016_M_VF".M_VF[#for_i].Orden_ConsignaManual;
ELSE
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan THEN
IF "DB2016_M_VF".M_VF[#for_i].Orden_ActivarAuto = TRUE AND NOT "DB2016_M_VF".M_VF[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2016_M_VF".M_VF[#for_i].Estado_SeguridadOk AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico THEN
#t_Activar := TRUE;
#t_Frecuencia := "DB2016_M_VF".M_VF[#for_i].Orden_ConsignaAuto;
"DB2016_M_VF".M_VF[#for_i].Orden_ConsignaManual := "DB2016_M_VF".M_VF[#for_i].Orden_ConsignaAuto;
ELSE
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
END_IF;
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual := "DB2016_M_VF".M_VF[#for_i].Estado_Activado;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// MOTOR SIMULADO
IF #Simulacion THEN
IF #t_Activar THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO TERMICO Y CONFIRMACION DE MARCHA
IF NOT #Simulacion AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha
THEN
IF NOT "DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico AND "DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO SOLO TERMICO
IF NOT #Simulacion AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND NOT "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF NOT "DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico AND #t_Activar THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO SOLO CONFIRMACION DE MARCHA
IF NOT #Simulacion AND NOT "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF "DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR SIN RETORNOS
IF NOT #Simulacion AND NOT "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND NOT "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF #t_Activar THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION ERRORES
// =============================================================================
// CHEQUEO RETORNO TERMICO
IF NOT #Simulacion AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND "DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico THEN
#t_ErrorTermico := TRUE;
ELSE
#t_ErrorTermico := FALSE;
END_IF;
// =============================================================================
// CHEQUEO RETORNO CONFIRMACION DE MARCHA
IF NOT #Simulacion AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha AND
(
(#t_Activar AND NOT "DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha)
OR
(NOT #t_Activar AND "DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha)
) THEN
#t_ErrorConfirmacionMarcha := TRUE;
ELSE
#t_ErrorConfirmacionMarcha := FALSE;
END_IF;
// =============================================================================
// TEMPORIZADO DE ERRORES
IF "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha
AND #t_ErrorConfirmacionMarcha
AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha
AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico
THEN
IF NOT #Simulacion AND #Pulso1seg THEN
"DB2016_M_VF".M_VF[#for_i].Tiempos_Alarma += 1;
END_IF;
ELSE
"DB2016_M_VF".M_VF[#for_i].Tiempos_Alarma := 0;
END_IF;
// =============================================================================
// ERROR TERMICO
IF #t_ErrorTermico THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_Termico := TRUE;
END_IF;
IF NOT #t_ErrorTermico THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_Termico := FALSE;
END_IF;
// =============================================================================
// ERROR CONFIRMACION DE MARCHA
IF "DB2016_M_VF".M_VF[#for_i].Tiempos_Alarma >= "DB2016_M_VF".M_VF[#for_i].Tiempos_SetPointAlarma AND #t_ErrorConfirmacionMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha := TRUE;
END_IF;
IF NOT #t_ErrorConfirmacionMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha := FALSE;
END_IF;
// =============================================================================
// ERROR GLOBAL
IF "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico OR
"DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha
THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_General := TRUE;
END_IF;
IF #Ack AND
NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico AND
NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha
THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_General := FALSE;
END_IF;
//"DB2016_M_VF".M_VF[#for_i].Alarmas_General := "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico OR "DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha OR "DB2016_M_VF".M_VF[#for_i].Alarmas_Escritura;
IF "DB2016_M_VF".M_VF[#for_i].Alarmas_General THEN
"DB2016_M_VF".Agrup["DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
// =============================================================================
// SI TENEMOS UN ERROR DESACTIVAMOS LA SALIDA
#t_Frecuencia := 0.0;
END_IF;
END_REGION ERRORES
REGION DIAGNOSIS
// =============================================================================
// CONTADOR DE HORAS DE FUNCIONAMIENTO
IF "DB2016_M_VF".M_VF[#for_i].Orden_ResetHoras THEN
"DB2016_M_VF".M_VF[#for_i].Orden_ResetHoras := FALSE;
END_IF;
IF NOT #Simulacion AND #Pulso1seg AND "DB2016_M_VF".M_VF[#for_i].Estado_Activado THEN
"DB2016_M_VF".M_VF[#for_i].Tiempos_Mantenimiento += 0.00027778;
END_IF;
// =============================================================================
// ALARMA HORAS PARA MANTENIMIENTO ALCANZADOS
"DB2016_M_VF".M_VF[#for_i].Alarmas_Mantenimiento := FALSE;
IF "DB2016_M_VF".M_VF[#for_i].Config_HabilitarMantenimiento
AND
"DB2016_M_VF".M_VF[#for_i].Tiempos_Mantenimiento > "DB2016_M_VF".M_VF[#for_i].Tiempos_SetPointMantenimiento
THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_Mantenimiento := TRUE;
END_IF;
END_REGION DIAGNOSIS
REGION ESCRITURA_SALIDA
CASE "DB2016_M_VF".M_VF[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA SALIDA
(* AREA: Pueden seleccionarse las siguientes áreas:16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#2: Salida de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección que se escribe, Solo se utilizan los 16 bits menos significativos.
BITOFFSET: Bit que se escribe,
VALUE: Valor que se escribe *)
IF "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaByte >= 0 THEN
IF #t_Activar AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_General THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Salida := TRUE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaBit,
value := TRUE);
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Salida := FALSE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaBit,
value := FALSE);
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
IF #t_Activar AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_General THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Salida := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Salida := FALSE;
END_IF;
END_CASE;
END_REGION ESCRITURA_SALIDA
REGION SALIDA
// =============================================================================
// TRASPASO DE PARAMETROS
"DB2016_M_VF".M_VF[#for_i].Estado_ValorActual := #t_Frecuencia;
// =============================================================================
// CONVERSIÓN DE VALORES
#t_y0 := INT_TO_REAL(0);
#t_y1 := INT_TO_REAL(27648);
#t_x := "DB2016_M_VF".M_VF[#for_i].Estado_ValorActual;
#t_x0 := "DB2016_M_VF".M_VF[#for_i].Config_EscaladoMinIngenieria;
#t_x1 := "DB2016_M_VF".M_VF[#for_i].Config_EscaladoMaxIngenieria;
// SI TENEMOS ORDEN DE ACTIVAR
IF #t_Activar THEN
// =============================================================================
// ESCALADO Y = (X-x0)*((y1-y0)/(x1-x0)) + y0
IF "DB2016_M_VF".M_VF[#for_i].Config_EscaladoMaxIngenieria > "DB2016_M_VF".M_VF[#for_i].Config_EscaladoMinIngenieria THEN
#t_ValorSalida := ((#t_x - #t_x0) * ((#t_y1 - #t_y0) / (#t_x1 - #t_x0))) + #t_y0;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Parametros := FALSE;
ELSE
#t_ValorSalida := 0.0;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Parametros := TRUE;
END_IF;
ELSE
#t_ValorSalida := 0.0;
END_IF;
REGION ESCRITURA_SALIDA
CASE "DB2016_M_VF".M_VF[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA DEL CANAL - POKE (area) 16#2 para CPU 1500 ; 16#82 para CPU 1200
IF "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaAnalogicaByte > 0 THEN
"DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta := REAL_TO_INT(#t_ValorSalida);
POKE(area := 16#82,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaAnalogicaByte,
value := INT_TO_WORD("DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta));
"DB2016_M_VF".M_VF[#for_i].Estado_Escritura := GET_ERR_ID();
IF "DB2016_M_VF".M_VF[#for_i].Estado_Escritura <> 0 THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_Escritura := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Alarmas_Escritura := FALSE;
END_IF;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta := 0;
END_IF;
#TIPO_ACCESO_PLC:
// =============================================================================
// ESCRITURA VALORES DE LA TARJETA
"DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta := REAL_TO_INT(#t_ValorSalida);
END_CASE;
END_REGION ESCRITURA_SALIDA
END_REGION SALIDA
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarAuto := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_Enclavado := FALSE;
"DB2016_M_VF".M_VF[#for_i].Orden_ConsignaAuto := 0.0;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan AND NOT "DB2016_M_VF".M_VF[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_16_DISP_M_VF",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_VF",
CodInt_2 := #for_i,
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 "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan AND "DB2016_M_VF".M_VF[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_16_DISP_M_VF",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_VF",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan AND "DB2016_M_VF".M_VF[#for_i].Estado_Activado AND NOT "DB2016_M_VF".M_VF[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_16_DISP_M_VF",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_VF",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan AND NOT "DB2016_M_VF".M_VF[#for_i].Estado_Activado AND "DB2016_M_VF".M_VF[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_16_DISP_M_VF",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_VF",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION TRAZABILIDAD_MANUALIZACION
// =============================================================================
// Estado anterior manual/automatico
"DB2016_M_VF".M_VF[#for_i].Aux_oldAutoMan := "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan;
"DB2016_M_VF".M_VF[#for_i].Aux_oldActivado := "DB2016_M_VF".M_VF[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2016_M_VF".M_VF[#for_i].Alarmas_General := FALSE;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Termico := FALSE;
"DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha := FALSE;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Mantenimiento := FALSE;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Escritura := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_Enclavado := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta := 0;
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarAuto := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
"DB2016_M_VF".M_VF[#for_i].Tiempos_Alarma := 0;
"DB2016_M_VF".M_VF[#for_i].Orden_ConsignaAuto := 0.0;
"DB2016_M_VF".M_VF[#for_i].Orden_ConsignaManual := 0.0;
"DB2016_M_VF".M_VF[#for_i].Estado_ValorActual := 0.0;
"DB2016_M_VF".M_VF[#for_i].Estado_AutoMan := FALSE;
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X0 := "DB2016_M_VF".M_VF[#for_i].Config_Habilitar;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X1 := "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X2 := "DB2016_M_VF".M_VF[#for_i].Estado_Enclavado;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X3 := "DB2016_M_VF".M_VF[#for_i].Estado_Activado;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X8 := "DB2016_M_VF".M_VF[#for_i].Alarmas_General;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X13 := "DB2016_M_VF".M_VF[#for_i].Alarmas_Mantenimiento;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION GESTION_HMI
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2016_M_VF".M_VF[#for_i].Alarmas_General AND NOT "DB2016_M_VF".M_VF[#for_i].Aux_oldAlarma THEN
"DB2016_M_VF".Agrup["DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2016_M_VF".M_VF[#for_i].Aux_oldAlarma := "DB2016_M_VF".M_VF[#for_i].Alarmas_General;
END_REGION GESTION_NUEVA_ALARMA
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2016_M_VF".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION LOGICA_DEL_DISPOSITIVO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,19 @@
MultiLingualTexts:
- id: MLC_4Pg
es-ES: FC2016_M_VF
- id: MLC_4yh
es-ES: Modelo de dispositivo motor variador
- id: MLC_4G3
es-ES: Pulso de 1 segundo
- id: MLC_3hd
es-ES: Simulacion
- id: MLC_cH
es-ES: Primer arranque
- id: MLC_33e
es-ES: Acuse general
- id: MLC_3GW
es-ES: Seguridades OK
- id: MLC_39w
es-ES: Usuario actual
- id: MLC_Nh
es-ES: Fecha y hora actual
@@ -1,694 +0,0 @@
FUNCTION "FC2016_ZC_M_VF" : Void
TITLE = FC2016_M_VF
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Modelo de dispositivo motor variador
VAR_INPUT
Pulso1seg : Bool; // Pulso de 1 segundo
Simulacion : Bool; // Simulacion
Arranque : Bool; // Primer arranque
Ack : Bool; // Acuse general
SeguridadesOK : Bool; // Seguridades OK
UsuarioActual : String; // Usuario actual
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Activar : Bool;
t_ErrorTermico : Bool;
t_ErrorConfirmacionMarcha : Bool;
t_Frecuencia : Real;
t_x : Real;
t_x0 : Real;
t_x1 : Real;
t_y0 : Real;
t_y1 : Real;
t_ValorSalida : Real;
END_VAR
VAR CONSTANT
TIPO_ACCESO_INDIRECTO : SInt := 0;
TIPO_ACCESO_PLC : SInt := 1;
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 Motor con variador de frecuencia.
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 tiempo en marcha
- Se gestiona la orden de escritura
- Se gestiona la trazabilidad de las manualizaciones del dispositivo
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO`, `FC15025_ZC_CONTADOR_TIEMPO` |
| FB | - |
| DB | DB2016_DISP_M_VF |
| UDT | ZC_DISP_M_VF, ZC_DISP_MUX_M_VF, `ZC_CONTADOR_TIEMPO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2020 | (ABH) | Primera version. |
| 00.00.02 | 28.11.2020 | (ABH) | Añadidas alarmas mantenimiento |
| 00.00.03 | 26.10.2021 | (ABH) | Se integra la gestion de directo/inverso |
| 00.00.04 | 26.04.2022 | (HCR) | Cambio en la organizacion del UDT. Se organiza en estructuras en base al tipo de funcion que realizan los datos. Se quita el enclavamiento para orden manual. |
| 00.00.05 | 27.04.2022 | (ABH) | Se añaden auxiliares de estado anterior del modo manual y automatico para poder registrar las trazas. |
| 00.00.06 | 27.04.2022 | (HCR) | Si no esta habilitado se quita orden manual y se quita el estado manual. Se agrega Ack para alarmas de confirmación de marcha. Se mantiene el estado cuando se pasa a manual. Cambio en lógica de errores. Cambio en la escritura de la salida. Cambio en laógica de modos |
| 00.00.07 | 08.11.2022 | (ABH) | Se añade trazabilidad manualizaciones |
| 00.00.08 | 08.11.2022 | (HCR) | Se añade trazabilidad de activación manual |
| 00.00.09 | 08.11.2022 | (HCR) | Cambio lógica enclavamiento |
| 00.00.10 | 18.09.2023 | (ABH) | Cambio en tipo de dato de horas de alarma. Se utiliza el tipo de dato ZC_CONTADOR_TIEMPO y el FC15025_ZC_CONTADOR_TIEMPO |
| 00.00.11 | 22.02.2024 | (ABH) | Se añade gestion multiplexado para Scada (no indexado) |
| 00.00.12 | 08.01.2025 | (ABH) | Se añade entrada de primer arranque del PLC para resetear indices de multiplexado Se añade movimiento de M[0] a multiplexado en caso de que el indice sea 0. |
| 00.00.13 | 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. |
| 00.00.14 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.15 | 24.04.2025 | (HCR) | Se añade gestion de idioma |
| 00.00.16 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traduccion. |
| 00.00.17 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza codigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. Se elimina nombre de UDT. Se añade indexHMI para lista de texto en sistemas de supervision. |
| 01.00.00 | 18.05.2026 | (ABH) | Se añade variable `Config_TipoAcceso` para uso de direccionamiento indirecto o uso directo en PLC. |
| 01.01.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2016_M_VF".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2016_M_VF".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2016_M_VF".NumDispositivos := "N_MAX_DISP_M_VF";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_M_VF" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_M_VF";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// DISPOSITIVO HABILITADO
// =============================================================================
IF "DB2016_M_VF".M_VF[#for_i].Config_Habilitar THEN
REGION LIMITE_GRUPO_ALARMA
IF "DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma < 0 THEN
"DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma := 0;
END_IF;
IF "DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma > "N_MAX_DISP_AGRUP" THEN
"DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma := "N_MAX_DISP_AGRUP";
END_IF;
END_REGION LIMITE_GRUPO_ALARMA
// =============================================================================
// PUESTA A CERO DE LA SALIDA
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
REGION LECTURA_VALORES
CASE "DB2016_M_VF".M_VF[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
(* AREA: Pueden seleccionarse las siguientes áreas: 16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#1: Entrada de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección en la que se lee. Solo se utilizan los 16 bits menos significativos.*)
IF "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoByte >= 0 THEN
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoBit);
IF "DB2016_M_VF".M_VF[#for_i].Config_HabilitarInvertirTermico THEN
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetTermicoBit));
END_IF;
END_IF;
// =============================================================================
// RETORNO CONFIRMACION DE MARCHA
IF "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaByte >= 0 THEN
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha := PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaBit);
IF "DB2016_M_VF".M_VF[#for_i].Config_HabilitarInvertirConfMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha := NOT (PEEK_BOOL(area := 16#81,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionRetConfMarchaBit));
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
;
END_CASE;
END_REGION LECTURA_VALORES
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan THEN
"DB2016_M_VF".Agrup["DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
IF "DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual = TRUE AND #SeguridadesOK AND "DB2016_M_VF".M_VF[#for_i].Estado_SeguridadOk AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico THEN //En caso de enclavar manual NOT "DB2037_TMF1_DISP_M_VF".M_VF[#i].Estado_Enclavado
#t_Activar := TRUE;
#t_Frecuencia := "DB2016_M_VF".M_VF[#for_i].Orden_ConsignaManual;
ELSE
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual := FALSE;
END_IF;
ELSE
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual := FALSE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan THEN
IF "DB2016_M_VF".M_VF[#for_i].Orden_ActivarAuto = TRUE AND NOT "DB2016_M_VF".M_VF[#for_i].Estado_Enclavado AND #SeguridadesOK AND "DB2016_M_VF".M_VF[#for_i].Estado_SeguridadOk AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico THEN
#t_Activar := TRUE;
#t_Frecuencia := "DB2016_M_VF".M_VF[#for_i].Orden_ConsignaAuto;
"DB2016_M_VF".M_VF[#for_i].Orden_ConsignaManual := "DB2016_M_VF".M_VF[#for_i].Orden_ConsignaAuto;
ELSE
#t_Activar := FALSE;
#t_Frecuencia := 0.0;
END_IF;
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual := "DB2016_M_VF".M_VF[#for_i].Estado_Activado;
END_IF;
END_REGION MODOS_DE_TRABAJO
REGION ESTADO_DEL_DISPOSITIVO
// =============================================================================
// MOTOR SIMULADO
IF #Simulacion THEN
IF #t_Activar THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO TERMICO Y CONFIRMACION DE MARCHA
IF NOT #Simulacion AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha
THEN
IF NOT "DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico AND "DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO SOLO TERMICO
IF NOT #Simulacion AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND NOT "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF NOT "DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico AND #t_Activar THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR RETORNO SOLO CONFIRMACION DE MARCHA
IF NOT #Simulacion AND NOT "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF "DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
// =============================================================================
// MOTOR SIN RETORNOS
IF NOT #Simulacion AND NOT "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND NOT "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha THEN
IF #t_Activar THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
END_IF;
END_IF;
END_REGION ESTADO_DEL_DISPOSITIVO
REGION ERRORES
// =============================================================================
// CHEQUEO RETORNO TERMICO
IF NOT #Simulacion AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoTermico
AND "DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico THEN
#t_ErrorTermico := TRUE;
ELSE
#t_ErrorTermico := FALSE;
END_IF;
// =============================================================================
// CHEQUEO RETORNO CONFIRMACION DE MARCHA
IF NOT #Simulacion AND "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha AND
(
(#t_Activar AND NOT "DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha)
OR
(NOT #t_Activar AND "DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha)
) THEN
#t_ErrorConfirmacionMarcha := TRUE;
ELSE
#t_ErrorConfirmacionMarcha := FALSE;
END_IF;
// =============================================================================
// TEMPORIZADO DE ERRORES
IF "DB2016_M_VF".M_VF[#for_i].Config_HabilitarRetornoConfMarcha
AND #t_ErrorConfirmacionMarcha
AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha
AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico
THEN
IF NOT #Simulacion AND #Pulso1seg THEN
"DB2016_M_VF".M_VF[#for_i].Tiempos_Alarma += 1;
END_IF;
ELSE
"DB2016_M_VF".M_VF[#for_i].Tiempos_Alarma := 0;
END_IF;
// =============================================================================
// ERROR TERMICO
IF #t_ErrorTermico THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_Termico := TRUE;
END_IF;
IF NOT #t_ErrorTermico THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_Termico := FALSE;
END_IF;
// =============================================================================
// ERROR CONFIRMACION DE MARCHA
IF "DB2016_M_VF".M_VF[#for_i].Tiempos_Alarma >= "DB2016_M_VF".M_VF[#for_i].Tiempos_SetPointAlarma AND #t_ErrorConfirmacionMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha := TRUE;
END_IF;
IF NOT #t_ErrorConfirmacionMarcha THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha := FALSE;
END_IF;
// =============================================================================
// ERROR GLOBAL
IF "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico OR
"DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha
THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_General := TRUE;
END_IF;
IF #Ack AND
NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico AND
NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha
THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_General := FALSE;
END_IF;
//"DB2016_M_VF".M_VF[#for_i].Alarmas_General := "DB2016_M_VF".M_VF[#for_i].Alarmas_Termico OR "DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha OR "DB2016_M_VF".M_VF[#for_i].Alarmas_Escritura;
IF "DB2016_M_VF".M_VF[#for_i].Alarmas_General THEN
"DB2016_M_VF".Agrup["DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma].AlgunaAlarma := TRUE;
// =============================================================================
// SI TENEMOS UN ERROR DESACTIVAMOS LA SALIDA
#t_Frecuencia := 0.0;
END_IF;
END_REGION ERRORES
REGION DIAGNOSIS
// =============================================================================
// CONTADOR DE HORAS DE FUNCIONAMIENTO
IF "DB2016_M_VF".M_VF[#for_i].Orden_ResetHoras THEN
"DB2016_M_VF".M_VF[#for_i].Orden_ResetHoras := FALSE;
END_IF;
IF NOT #Simulacion AND #Pulso1seg AND "DB2016_M_VF".M_VF[#for_i].Estado_Activado THEN
"DB2016_M_VF".M_VF[#for_i].Tiempos_Mantenimiento += 0.00027778;
END_IF;
// =============================================================================
// ALARMA HORAS PARA MANTENIMIENTO ALCANZADOS
"DB2016_M_VF".M_VF[#for_i].Alarmas_Mantenimiento := FALSE;
IF "DB2016_M_VF".M_VF[#for_i].Config_HabilitarMantenimiento
AND
"DB2016_M_VF".M_VF[#for_i].Tiempos_Mantenimiento > "DB2016_M_VF".M_VF[#for_i].Tiempos_SetPointMantenimiento
THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_Mantenimiento := TRUE;
END_IF;
END_REGION DIAGNOSIS
REGION ESCRITURA_SALIDA
CASE "DB2016_M_VF".M_VF[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA SALIDA
(* AREA: Pueden seleccionarse las siguientes áreas:16#81: Input, 16#82: Output, 16#83: Marcas, 16#84: DB, 16#2: Salida de periferia (solo S7-1500)
DBNUMBER: Número del bloque de datos si AREA = DB, de lo contrario "0"
BYTEOFFSET: Dirección que se escribe, Solo se utilizan los 16 bits menos significativos.
BITOFFSET: Bit que se escribe,
VALUE: Valor que se escribe *)
IF "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaByte >= 0 THEN
IF #t_Activar AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_General THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Salida := TRUE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaBit,
value := TRUE);
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Salida := FALSE;
POKE_BOOL(area := 16#82,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaByte,
bitOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaBit,
value := FALSE);
END_IF;
END_IF;
#TIPO_ACCESO_PLC:
IF #t_Activar AND NOT "DB2016_M_VF".M_VF[#for_i].Alarmas_General THEN
"DB2016_M_VF".M_VF[#for_i].Estado_Salida := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_Salida := FALSE;
END_IF;
END_CASE;
END_REGION ESCRITURA_SALIDA
REGION SALIDA
// =============================================================================
// TRASPASO DE PARAMETROS
"DB2016_M_VF".M_VF[#for_i].Estado_ValorActual := #t_Frecuencia;
// =============================================================================
// CONVERSIÓN DE VALORES
#t_y0 := INT_TO_REAL(0);
#t_y1 := INT_TO_REAL(27648);
#t_x := "DB2016_M_VF".M_VF[#for_i].Estado_ValorActual;
#t_x0 := "DB2016_M_VF".M_VF[#for_i].Config_EscaladoMinIngenieria;
#t_x1 := "DB2016_M_VF".M_VF[#for_i].Config_EscaladoMaxIngenieria;
// SI TENEMOS ORDEN DE ACTIVAR
IF #t_Activar THEN
// =============================================================================
// ESCALADO Y = (X-x0)*((y1-y0)/(x1-x0)) + y0
IF "DB2016_M_VF".M_VF[#for_i].Config_EscaladoMaxIngenieria > "DB2016_M_VF".M_VF[#for_i].Config_EscaladoMinIngenieria THEN
#t_ValorSalida := ((#t_x - #t_x0) * ((#t_y1 - #t_y0) / (#t_x1 - #t_x0))) + #t_y0;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Parametros := FALSE;
ELSE
#t_ValorSalida := 0.0;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Parametros := TRUE;
END_IF;
ELSE
#t_ValorSalida := 0.0;
END_IF;
REGION ESCRITURA_SALIDA
CASE "DB2016_M_VF".M_VF[#for_i].Config_TipoAcceso OF
#TIPO_ACCESO_INDIRECTO:
// =============================================================================
// ESCRITURA DEL CANAL - POKE (area) 16#2 para CPU 1500 ; 16#82 para CPU 1200
IF "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaAnalogicaByte > 0 THEN
"DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta := REAL_TO_INT(#t_ValorSalida);
POKE(area := 16#82,
dbNumber := 0,
byteOffset := "DB2016_M_VF".M_VF[#for_i].Config_DireccionSalidaAnalogicaByte,
value := INT_TO_WORD("DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta));
"DB2016_M_VF".M_VF[#for_i].Estado_Escritura := GET_ERR_ID();
IF "DB2016_M_VF".M_VF[#for_i].Estado_Escritura <> 0 THEN
"DB2016_M_VF".M_VF[#for_i].Alarmas_Escritura := TRUE;
ELSE
"DB2016_M_VF".M_VF[#for_i].Alarmas_Escritura := FALSE;
END_IF;
ELSE
"DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta := 0;
END_IF;
#TIPO_ACCESO_PLC:
// =============================================================================
// ESCRITURA VALORES DE LA TARJETA
"DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta := REAL_TO_INT(#t_ValorSalida);
END_CASE;
END_REGION ESCRITURA_SALIDA
END_REGION SALIDA
// =============================================================================
// RESET ORDEN ACTIVAR EN AUTO
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarAuto := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_Enclavado := FALSE;
"DB2016_M_VF".M_VF[#for_i].Orden_ConsignaAuto := 0.0;
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan AND NOT "DB2016_M_VF".M_VF[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_16_DISP_M_VF",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_VF",
CodInt_2 := #for_i,
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 "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan AND "DB2016_M_VF".M_VF[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_16_DISP_M_VF",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_VF",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan AND "DB2016_M_VF".M_VF[#for_i].Estado_Activado AND NOT "DB2016_M_VF".M_VF[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_16_DISP_M_VF",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_VF",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan AND NOT "DB2016_M_VF".M_VF[#for_i].Estado_Activado AND "DB2016_M_VF".M_VF[#for_i].Aux_oldActivado THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_16_DISP_M_VF",
User := #UsuarioActual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_M_VF",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION TRAZABILIDAD_MANUALIZACION
// =============================================================================
// Estado anterior manual/automatico
"DB2016_M_VF".M_VF[#for_i].Aux_oldAutoMan := "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan;
"DB2016_M_VF".M_VF[#for_i].Aux_oldActivado := "DB2016_M_VF".M_VF[#for_i].Estado_Activado;
ELSE
// =============================================================================
// DISPOSITIVO NO HABILITADO
// =============================================================================
"DB2016_M_VF".M_VF[#for_i].Alarmas_General := FALSE;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Termico := FALSE;
"DB2016_M_VF".M_VF[#for_i].Alarmas_ConfirmacionMarcha := FALSE;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Mantenimiento := FALSE;
"DB2016_M_VF".M_VF[#for_i].Alarmas_Escritura := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaTermico := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_EntradaConfMarcha := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_Enclavado := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_ValorTarjeta := 0;
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarAuto := FALSE;
"DB2016_M_VF".M_VF[#for_i].Estado_Activado := FALSE;
"DB2016_M_VF".M_VF[#for_i].Tiempos_Alarma := 0;
"DB2016_M_VF".M_VF[#for_i].Orden_ConsignaAuto := 0.0;
"DB2016_M_VF".M_VF[#for_i].Orden_ConsignaManual := 0.0;
"DB2016_M_VF".M_VF[#for_i].Estado_ValorActual := 0.0;
"DB2016_M_VF".M_VF[#for_i].Estado_AutoMan := FALSE;
"DB2016_M_VF".M_VF[#for_i].Orden_ActivarManual := FALSE;
END_IF;
REGION GESTION_HMI
// =============================================================================
// GESTION BITS PARA VISUALIZACION
// Bit 0 Habilitacion
// Bit 1 ManualAutomatico
// Bit 2 Enclavado
// Bit 3 Activado 1
// Bit 4 Activado 2
// Bit 5 Reserva
// Bit 6 Reserva
// Bit 7 Reserva
// Bit 8 Alarma General
// Bit 9 Alarma 1
// Bit 10 Alarma 2
// Bit 11 Alarma 3
// Bit 12 Alarma 4
// Bit 13 Alarma Mantenimiento
// Bit 14 Reserva
// Bit 15 Reserva
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X0 := "DB2016_M_VF".M_VF[#for_i].Config_Habilitar;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X1 := "DB2016_M_VF".M_VF[#for_i].Estado_AutoMan;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X2 := "DB2016_M_VF".M_VF[#for_i].Estado_Enclavado;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X3 := "DB2016_M_VF".M_VF[#for_i].Estado_Activado;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X4 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X5 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X6 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X7 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X8 := "DB2016_M_VF".M_VF[#for_i].Alarmas_General;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X9 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X10 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X11 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X12 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X13 := "DB2016_M_VF".M_VF[#for_i].Alarmas_Mantenimiento;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X14 := FALSE;
"DB2016_M_VF".M_VF[#for_i].Hmi_Estado.%X15 := FALSE;
END_REGION GESTION_HMI
REGION GESTION_NUEVA_ALARMA
// =============================================================================
// DETECCION DE NUEVA ALARMA
IF "DB2016_M_VF".M_VF[#for_i].Alarmas_General AND NOT "DB2016_M_VF".M_VF[#for_i].Aux_oldAlarma THEN
"DB2016_M_VF".Agrup["DB2016_M_VF".M_VF[#for_i].Config_GrupoAlarma].NuevaAlarma := TRUE;
END_IF;
// Estado anterior de las alarmas
"DB2016_M_VF".M_VF[#for_i].Aux_oldAlarma := "DB2016_M_VF".M_VF[#for_i].Alarmas_General;
END_REGION GESTION_NUEVA_ALARMA
END_FOR;
// =============================================================================
// ACUSE DE NUEVAS ALARMAS
IF #Ack THEN
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2016_M_VF".Agrup[#for_i].NuevaAlarma := FALSE;
END_FOR;
END_IF;
END_REGION LOGICA_DEL_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,349 @@
{
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
@@ -0,0 +1,15 @@
MultiLingualTexts:
- id: MLC_3tF
es-ES: FC2025_TOT
- id: MLC_ax
es-ES: Bloque de funcion para gestion de totalizadores.
- id: MLC_4Yz
es-ES: Pulso de 100 milisegundos
- id: MLC_39T
es-ES: Simulacion
- id: MLC_nD
es-ES: Primer arranque
- id: MLC_4ux
es-ES: VALOR MÁXIMO DE ACUMULADOR
- id: MLC_4VC
es-ES: VALOR MÁXIMO NUMERO DE DESBORDE
@@ -1,342 +0,0 @@
FUNCTION "FC2025_ZC_TOT" : Void
TITLE = FC2025_TOT
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Bloque de funcion para gestion de totalizadores.
VAR_INPUT
Pulso100ms : Bool; // Pulso de 100 milisegundos
Simulacion : Bool; // Simulacion
Arranque : Bool; // Primer arranque
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
VALOR_MAXIMO_CONTADORES : Real := 1000000.0; // VALOR MÁXIMO DE ACUMULADOR
NUMERO_MAXIMO_DESBORDE : Int := 32000; // VALOR MÁXIMO NUMERO DE DESBORDE
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 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_FUNCTION
@@ -0,0 +1,227 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_sz";
S7_BlockNumber := "2026";
S7_BlockTitle := "MLC_5BL";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2026_ZC_FF" : Void
VAR_INPUT
{ S7_MLC := "MLC_NU" }
Pulso1Seg : Bool;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
END_VAR
VAR CONSTANT
REPOSO : SInt := 0;
FLOP : SInt := 10;
PARO1 : SInt := 20;
FLIP : SInt := 30;
PARO2 : SInt := 40;
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 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 | `DB2026_FF` |
| UDT | `UDT_ZC_DISP_FF_PARO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 10.08.2020 | (ABH) | Primera version. |
| 00.00.02 | 02.10.2020 | (ABH) | Cambio en la logica del dispositivo, ya que al cambio de flip a flop, se perdia un segundo del contaje de tienmpo. |
| 00.00.03 | 28.11.2023 | (ABH) | Se añade condicion de activacion del Flip Flop a que algun SP sea mayor que 0.. |
| 00.00.04 | 15.10.2025 | (ABH) | Se añade orden pausa para dejar de contar. |
| 01.00.00 | 06.07.2026 | (ABH) | Refactorizacion de logica para optimizacion. Se añade ordenes de paro (opcionales) para su uso para evitar golpes de ariete. |
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_FF" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_FF";
END_REGION COMPROBACION_N_MAX
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
FOR #for_i := 1 TO #t_N_MAX DO
// ==========================================================================================================
REGION COMPROBACIONES_INICIALES
// Si no hay orden o faltan tiempos, reseteamos valores y salimos de la funcion
IF NOT "DB2026_FF".FF[#for_i].Orden_Activar OR ("DB2026_FF".FF[#for_i].Config_SetPointTiempoFlip <= 0 AND "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlop <= 0) THEN
"DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
"DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
"DB2026_FF".FF[#for_i].Estado_Etapa := #REPOSO;
"DB2026_FF".FF[#for_i].Aux_Inicializado := FALSE;
RETURN;
END_IF;
END_REGION COMPROBACIONES_INICIALES
// ==========================================================================================================
REGION INICIALIZACION
// Arranque inicial forzado a Flop si no hay estado activo
IF NOT "DB2026_FF".FF[#for_i].Aux_Inicializado THEN
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
"DB2026_FF".FF[#for_i].Aux_Inicializado := TRUE;
END_IF;
END_REGION INICIALIZACION
// ==========================================================================================================
REGION LOGICA_DE_ESTADOS
CASE "DB2026_FF".FF[#for_i].Estado_Etapa OF
#FLOP:
// Salidas
"DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
"DB2026_FF".FF[#for_i].Estado_Flop := TRUE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlop THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
// Si no tenemos tiempo de paro configurado, saltamos a FLIP
IF "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo <= 0 THEN
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLIP;
ELSE
"DB2026_FF".FF[#for_i].Estado_Etapa := #PARO1;
END_IF;
END_IF;
#PARO1:
// Salidas
"DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
"DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := TRUE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLIP;
END_IF;
#FLIP:
// Salidas
"DB2026_FF".FF[#for_i].Estado_Flip := TRUE;
"DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlip THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
// Si no tenemos tiempo de paro configurado, saltamos a FLOP
IF "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo <= 0 THEN
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
ELSE
"DB2026_FF".FF[#for_i].Estado_Etapa := #PARO2;
END_IF;
END_IF;
#PARO2:
// Salidas
"DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
"DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := TRUE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
END_IF;
ELSE
"DB2026_FF".FF[#for_i].Estado_Etapa := #REPOSO;
END_CASE;
END_REGION LOGICA_DE_ESTADOS
END_FOR;
END_REGION GESTION_DISPOSITIVO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_5BL
es-ES: FC2026_ZC_FF
- id: MLC_sz
es-ES: Funcion para gestion de flip flop
- id: MLC_NU
es-ES: Pulso de 1 segundo
@@ -1,224 +0,0 @@
FUNCTION "FC2026_ZC_FF" : Void
TITLE = FC2026_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_TEMP
for_i : Int;
t_N_MAX : Int;
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 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 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 | `DB2026_FF` |
| UDT | `UDT_ZC_DISP_FF_PARO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 10.08.2020 | (ABH) | Primera version. |
| 00.00.02 | 02.10.2020 | (ABH) | Cambio en la logica del dispositivo, ya que al cambio de flip a flop, se perdia un segundo del contaje de tienmpo. |
| 00.00.03 | 28.11.2023 | (ABH) | Se añade condicion de activacion del Flip Flop a que algun SP sea mayor que 0.. |
| 00.00.04 | 15.10.2025 | (ABH) | Se añade orden pausa para dejar de contar. |
| 01.00.00 | 06.07.2026 | (ABH) | Refactorizacion de logica para optimizacion. Se añade ordenes de paro (opcionales) para su uso para evitar golpes de ariete. |
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_FF" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_FF";
END_REGION COMPROBACION_N_MAX
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
FOR #for_i := 1 TO #t_N_MAX DO
// ==========================================================================================================
REGION COMPROBACIONES_INICIALES
// Si no hay orden o faltan tiempos, reseteamos valores y salimos de la funcion
IF NOT "DB2026_FF".FF[#for_i].Orden_Activar OR ("DB2026_FF".FF[#for_i].Config_SetPointTiempoFlip <= 0 AND "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlop <= 0) THEN
"DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
"DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
"DB2026_FF".FF[#for_i].Estado_Etapa := #REPOSO;
"DB2026_FF".FF[#for_i].Aux_Inicializado := FALSE;
RETURN;
END_IF;
END_REGION COMPROBACIONES_INICIALES
// ==========================================================================================================
REGION INICIALIZACION
// Arranque inicial forzado a Flop si no hay estado activo
IF NOT "DB2026_FF".FF[#for_i].Aux_Inicializado THEN
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
"DB2026_FF".FF[#for_i].Aux_Inicializado := TRUE;
END_IF;
END_REGION INICIALIZACION
// ==========================================================================================================
REGION LOGICA_DE_ESTADOS
CASE "DB2026_FF".FF[#for_i].Estado_Etapa OF
#FLOP:
// Salidas
"DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
"DB2026_FF".FF[#for_i].Estado_Flop := TRUE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlop THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
// Si no tenemos tiempo de paro configurado, saltamos a FLIP
IF "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo <= 0 THEN
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLIP;
ELSE
"DB2026_FF".FF[#for_i].Estado_Etapa := #PARO1;
END_IF;
END_IF;
#PARO1:
// Salidas
"DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
"DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := TRUE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLIP;
END_IF;
#FLIP:
// Salidas
"DB2026_FF".FF[#for_i].Estado_Flip := TRUE;
"DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := FALSE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoFlip THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
// Si no tenemos tiempo de paro configurado, saltamos a FLOP
IF "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo <= 0 THEN
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
ELSE
"DB2026_FF".FF[#for_i].Estado_Etapa := #PARO2;
END_IF;
END_IF;
#PARO2:
// Salidas
"DB2026_FF".FF[#for_i].Estado_Flip := FALSE;
"DB2026_FF".FF[#for_i].Estado_Flop := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro1 := FALSE;
"DB2026_FF".FF[#for_i].Estado_Paro2 := TRUE;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2026_FF".FF[#for_i].Orden_Pausa THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2026_FF".FF[#for_i].Aux_Tiempo >= "DB2026_FF".FF[#for_i].Config_SetPointTiempoParo THEN
"DB2026_FF".FF[#for_i].Aux_Tiempo := 0;
"DB2026_FF".FF[#for_i].Estado_Etapa := #FLOP;
END_IF;
ELSE
"DB2026_FF".FF[#for_i].Estado_Etapa := #REPOSO;
END_CASE;
END_REGION LOGICA_DE_ESTADOS
END_FOR;
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,216 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4vc";
S7_BlockNumber := "2027";
S7_BlockTitle := "MLC_4rR";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2027_ZC_FF_SEC" : Void
VAR_INPUT
{ S7_MLC := "MLC_3qK" }
Pulso1Seg : Bool;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
for_j : Int;
t_SumaFlip : Int;
END_VAR
VAR CONSTANT
REPOSO : SInt := 0;
FLOP : SInt := 10;
FLIP : SInt := 20;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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 | `DB2027_FF_SEC` |
| 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 COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_FF_SEC" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_FF_SEC";
END_REGION COMPROBACION_N_MAX
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
FOR #for_i := 1 TO #t_N_MAX DO
// ==========================================================================================================
REGION COMPROBACIONES_INICIALES
// Suma de seguridad por FOR (Determinista)
#t_SumaFlip := 0;
FOR #for_j := 1 TO 10 DO
#t_SumaFlip += "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlip[#for_j];
END_FOR;
// Si no hay orden o los tiempos globales son 0, reseteamos valores y salimos de la funcion
IF NOT "DB2027_FF_SEC".FF_SEC[#for_i].Orden_Activar OR (#t_SumaFlip <= 0 AND "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlop <= 0) THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlop := FALSE;
FOR #for_j := 1 TO 10 DO
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlip[#for_j] := FALSE;
END_FOR;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #REPOSO;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Inicializacion := FALSE;
RETURN;
END_IF;
END_REGION COMPROBACIONES_INICIALES
// ==========================================================================================================
REGION INICIALIZACION
// Arranque inicial forzado a Flop si no hay estado activo
IF NOT "DB2027_FF_SEC".FF_SEC[#for_i].Aux_Inicializacion THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #FLOP;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Inicializacion := TRUE;
END_IF;
END_REGION INICIALIZACION
// ==========================================================================================================
REGION LOGICA_DE_ESTADOS
CASE "DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa OF
#FLOP:
// Salidas
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlop := TRUE;
FOR #for_j := 1 TO 10 DO
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlip[#for_j] := FALSE;
END_FOR;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2027_FF_SEC".FF_SEC[#for_i].Orden_Pausa THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo >= "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlop THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := 11; // Fallback por defecto
// Buscar primer Flip válido
FOR #for_j := 1 TO 10 DO
IF "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlip[#for_j] > 0 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := #for_j;
EXIT; // Corta el FOR en el primer acierto
END_IF;
END_FOR;
// Transición si encontró algo válido
IF "DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual <= 10 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #FLIP;
END_IF;
END_IF;
#FLIP:
// Salidas
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlop := FALSE;
FOR #for_j := 1 TO 10 DO
IF #for_j = "DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlip[#for_j] := TRUE;
ELSE
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlip[#for_j] := FALSE;
END_IF;
END_FOR;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2027_FF_SEC".FF_SEC[#for_i].Orden_Pausa THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo += 1;
END_IF;
/// Transición
IF "DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo >= "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlip["DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual] THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual += 1; // Apunta al siguiente
// Búsqueda del siguiente Flip válido
FOR #for_j := "DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual TO 10 DO
IF "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlip[#for_j] > 0 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := #for_j;
EXIT;
END_IF;
// Si llega al final sin encontrar nada
IF #for_j = 10 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := 11;
END_IF;
END_FOR;
// Si rebasó el límite, vuelve al Flop
IF "DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual > 10 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #FLOP;
END_IF;
END_IF;
ELSE
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #REPOSO;
END_CASE;
END_REGION LOGICA_DE_ESTADOS
END_FOR;
END_REGION GESTION_DISPOSITIVO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_4rR
es-ES: FC2027_ZC_FF_SEC
- id: MLC_4vc
es-ES: Funcion para gestion de Flip Flop secuencial
- id: MLC_3qK
es-ES: Pulso 1 segundo
@@ -1,213 +0,0 @@
FUNCTION "FC2027_ZC_FF_SEC" : Void
TITLE = FC2027_ZC_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_TEMP
for_i : Int;
t_N_MAX : Int;
for_j : 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 | `DB2027_FF_SEC` |
| 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 COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_FF_SEC" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_FF_SEC";
END_REGION COMPROBACION_N_MAX
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
FOR #for_i := 1 TO #t_N_MAX DO
// ==========================================================================================================
REGION COMPROBACIONES_INICIALES
// Suma de seguridad por FOR (Determinista)
#t_SumaFlip := 0;
FOR #for_j := 1 TO 10 DO
#t_SumaFlip += "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlip[#for_j];
END_FOR;
// Si no hay orden o los tiempos globales son 0, reseteamos valores y salimos de la funcion
IF NOT "DB2027_FF_SEC".FF_SEC[#for_i].Orden_Activar OR (#t_SumaFlip <= 0 AND "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlop <= 0) THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlop := FALSE;
FOR #for_j := 1 TO 10 DO
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlip[#for_j] := FALSE;
END_FOR;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #REPOSO;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Inicializacion := FALSE;
RETURN;
END_IF;
END_REGION COMPROBACIONES_INICIALES
// ==========================================================================================================
REGION INICIALIZACION
// Arranque inicial forzado a Flop si no hay estado activo
IF NOT "DB2027_FF_SEC".FF_SEC[#for_i].Aux_Inicializacion THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #FLOP;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Inicializacion := TRUE;
END_IF;
END_REGION INICIALIZACION
// ==========================================================================================================
REGION LOGICA_DE_ESTADOS
CASE "DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa OF
#FLOP:
// Salidas
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlop := TRUE;
FOR #for_j := 1 TO 10 DO
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlip[#for_j] := FALSE;
END_FOR;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2027_FF_SEC".FF_SEC[#for_i].Orden_Pausa THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo += 1;
END_IF;
// Transición
IF "DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo >= "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlop THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := 11; // Fallback por defecto
// Buscar primer Flip válido
FOR #for_j := 1 TO 10 DO
IF "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlip[#for_j] > 0 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := #for_j;
EXIT; // Corta el FOR en el primer acierto
END_IF;
END_FOR;
// Transición si encontró algo válido
IF "DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual <= 10 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #FLIP;
END_IF;
END_IF;
#FLIP:
// Salidas
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlop := FALSE;
FOR #for_j := 1 TO 10 DO
IF #for_j = "DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlip[#for_j] := TRUE;
ELSE
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_SalidaFlip[#for_j] := FALSE;
END_IF;
END_FOR;
// Conteo de tiempo
IF #Pulso1Seg AND NOT "DB2027_FF_SEC".FF_SEC[#for_i].Orden_Pausa THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo += 1;
END_IF;
/// Transición
IF "DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo >= "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlip["DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual] THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_Tiempo := 0;
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual += 1; // Apunta al siguiente
// Búsqueda del siguiente Flip válido
FOR #for_j := "DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual TO 10 DO
IF "DB2027_FF_SEC".FF_SEC[#for_i].Config_SetPointTiempoFlip[#for_j] > 0 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := #for_j;
EXIT;
END_IF;
// Si llega al final sin encontrar nada
IF #for_j = 10 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual := 11;
END_IF;
END_FOR;
// Si rebasó el límite, vuelve al Flop
IF "DB2027_FF_SEC".FF_SEC[#for_i].Aux_FlipActual > 10 THEN
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #FLOP;
END_IF;
END_IF;
ELSE
"DB2027_FF_SEC".FF_SEC[#for_i].Estado_Etapa := #REPOSO;
END_CASE;
END_REGION LOGICA_DE_ESTADOS
END_FOR;
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,219 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_3A2";
S7_BlockNumber := "2028";
S7_BlockTitle := "MLC_3SV";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2028_ZC_TEMP" : Void
VAR_INPUT
Pulso1seg : Bool;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
END_VAR
{ S7_Language := "SCL" }
NETWORK
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2022 - 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 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 | `DB2028_TEMP` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 02.10.2022 | (ABH) | Primera version. |
| 00.00.02 | 03.05.2022 | (ABH) | TONR. Se añade tiempo Restante. |
| 00.00.03 | 13.09.2023 | (ABH) | TP. Se elimina la marca "Flanco" ya que no es necesaria. Se utiliza solamente la marca "oldIN" para la deteccion de flancos. |
| 00.00.04 | 13.08.2025 | (ABH) | TONR. Se modifica activacion de salida Q sin dependencia de entrada IN. |
| 01.00.00 | 06.07.2026 | (ABH) | Se unifican los temporizadores en un solo DB y en un solo FC |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO_TON
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TON";
IF #t_N_MAX >= 1 THEN
FOR #for_i := 1 TO #t_N_MAX DO
IF "DB2028_TEMP".TON[#for_i].IN THEN
IF "DB2028_TEMP".TON[#for_i].ET >= "DB2028_TEMP".TON[#for_i].SP THEN
"DB2028_TEMP".TON[#for_i].Q := TRUE;
ELSE
IF #Pulso1seg THEN
"DB2028_TEMP".TON[#for_i].ET += 1;
END_IF;
END_IF;
ELSE
"DB2028_TEMP".TON[#for_i].ET := 0;
"DB2028_TEMP".TON[#for_i].Q := FALSE;
END_IF;
END_FOR;
END_IF;
END_REGION GESTION_DISPOSITIVO_TON
// ==========================================================================================================
REGION GESTION_DISPOSITIVO_TOF
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TOF";
IF #t_N_MAX >= 1 THEN
FOR #for_i := 1 TO #t_N_MAX DO
IF "DB2028_TEMP".TOF[#for_i].IN THEN
"DB2028_TEMP".TOF[#for_i].OldActivar := TRUE;
"DB2028_TEMP".TOF[#for_i].Q := TRUE;
END_IF;
IF "DB2028_TEMP".TOF[#for_i].OldActivar AND NOT "DB2028_TEMP".TOF[#for_i].IN THEN
IF "DB2028_TEMP".TOF[#for_i].ET <= 0 THEN
"DB2028_TEMP".TOF[#for_i].Q := FALSE;
"DB2028_TEMP".TOF[#for_i].OldActivar := FALSE;
ELSE
IF #Pulso1seg THEN
"DB2028_TEMP".TOF[#for_i].ET -= 1;
END_IF;
END_IF;
ELSE
"DB2028_TEMP".TOF[#for_i].ET := "DB2028_TEMP".TOF[#for_i].SP;
END_IF;
END_FOR;
END_IF;
END_REGION GESTION_DISPOSITIVO_TOF
// ==========================================================================================================
REGION GESTION_DISPOSITIVO_TONR
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TONR";
IF #t_N_MAX >= 1 THEN
FOR #for_i := 1 TO #t_N_MAX DO
IF "DB2028_TEMP".TONR[#for_i].RESET THEN
"DB2028_TEMP".TONR[#for_i].ET := 0;
"DB2028_TEMP".TONR[#for_i].Q := FALSE;
"DB2028_TEMP".TONR[#for_i].RESET := FALSE;
END_IF;
IF "DB2028_TEMP".TONR[#for_i].IN AND
"DB2028_TEMP".TONR[#for_i].ET < "DB2028_TEMP".TONR[#for_i].SP AND
#Pulso1seg
THEN
"DB2028_TEMP".TONR[#for_i].ET += 1;
"DB2028_TEMP".TONR[#for_i].Q := FALSE;
END_IF;
IF "DB2028_TEMP".TONR[#for_i].ET >= "DB2028_TEMP".TONR[#for_i].SP THEN
"DB2028_TEMP".TONR[#for_i].Q := TRUE;
END_IF;
"DB2028_TEMP".TONR[#for_i].RT := "DB2028_TEMP".TONR[#for_i].SP - "DB2028_TEMP".TONR[#for_i].ET;
END_FOR;
END_IF;
END_REGION GESTION_DISPOSITIVO_TONR
// ==========================================================================================================
REGION GESTION_DISPOSITIVO_TP
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TP";
IF #t_N_MAX >= 1 THEN
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// Detección de flanco
IF "DB2028_TEMP".TP[#for_i].IN AND NOT "DB2028_TEMP".TP[#for_i].oldIN THEN
"DB2028_TEMP".TP[#for_i].Q := TRUE;
END_IF;
// Actualizo valor de memoria
"DB2028_TEMP".TP[#for_i].oldIN := "DB2028_TEMP".TP[#for_i].IN;
// =============================================================================
// Reinicio memoria de activación cuando piedo salida y cuento tiempo
IF NOT "DB2028_TEMP".TP[#for_i].IN THEN
"DB2028_TEMP".TP[#for_i].oldIN := FALSE;
END_IF;
// =============================================================================
// Incremento de tiempo
IF "DB2028_TEMP".TP[#for_i].Q
AND
"DB2028_TEMP".TP[#for_i].ET < "DB2028_TEMP".TP[#for_i].PT
AND
#Pulso1seg
THEN
"DB2028_TEMP".TP[#for_i].ET += 1;
END_IF;
// =============================================================================
// Desactivo salida cuando cumplo tiempo de retardo de salida
IF "DB2028_TEMP".TP[#for_i].ET >= "DB2028_TEMP".TP[#for_i].PT THEN
"DB2028_TEMP".TP[#for_i].Q := FALSE;
"DB2028_TEMP".TP[#for_i].ET := 0;
END_IF;
END_FOR;
END_IF;
END_REGION GESTION_DISPOSITIVO_TP
END_NETWORK
END_FUNCTION
@@ -0,0 +1,5 @@
MultiLingualTexts:
- id: MLC_3SV
es-ES: FC2028_ZC_TEMP
- id: MLC_3A2
es-ES: Funcion para gestion de temporizadorres TON, TOF, TONR y TP (Base 1 seg)
@@ -1,216 +0,0 @@
FUNCTION "FC2028_ZC_TEMP" : Void
TITLE = FC2028_ZC_TEMP
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion de temporizadorres TON, TOF, TONR y TP (Base 1 seg)
VAR_INPUT
Pulso1seg : Bool;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
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 |
---
> **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 | `DB2028_TEMP` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 02.10.2022 | (ABH) | Primera version. |
| 00.00.02 | 03.05.2022 | (ABH) | TONR. Se añade tiempo Restante. |
| 00.00.03 | 13.09.2023 | (ABH) | TP. Se elimina la marca "Flanco" ya que no es necesaria. Se utiliza solamente la marca "oldIN" para la deteccion de flancos. |
| 00.00.04 | 13.08.2025 | (ABH) | TONR. Se modifica activacion de salida Q sin dependencia de entrada IN. |
| 01.00.00 | 06.07.2026 | (ABH) | Se unifican los temporizadores en un solo DB y en un solo FC |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_DISPOSITIVO_TON
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TON";
IF #t_N_MAX >= 1 THEN
FOR #for_i := 1 TO #t_N_MAX DO
IF "DB2028_TEMP".TON[#for_i].IN THEN
IF "DB2028_TEMP".TON[#for_i].ET >= "DB2028_TEMP".TON[#for_i].SP THEN
"DB2028_TEMP".TON[#for_i].Q := TRUE;
ELSE
IF #Pulso1seg THEN
"DB2028_TEMP".TON[#for_i].ET += 1;
END_IF;
END_IF;
ELSE
"DB2028_TEMP".TON[#for_i].ET := 0;
"DB2028_TEMP".TON[#for_i].Q := FALSE;
END_IF;
END_FOR;
END_IF;
END_REGION GESTION_DISPOSITIVO_TON
// ==========================================================================================================
REGION GESTION_DISPOSITIVO_TOF
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TOF";
IF #t_N_MAX >= 1 THEN
FOR #for_i := 1 TO #t_N_MAX DO
IF "DB2028_TEMP".TOF[#for_i].IN THEN
"DB2028_TEMP".TOF[#for_i].OldActivar := TRUE;
"DB2028_TEMP".TOF[#for_i].Q := TRUE;
END_IF;
IF "DB2028_TEMP".TOF[#for_i].OldActivar AND NOT "DB2028_TEMP".TOF[#for_i].IN THEN
IF "DB2028_TEMP".TOF[#for_i].ET <= 0 THEN
"DB2028_TEMP".TOF[#for_i].Q := FALSE;
"DB2028_TEMP".TOF[#for_i].OldActivar := FALSE;
ELSE
IF #Pulso1seg THEN
"DB2028_TEMP".TOF[#for_i].ET -= 1;
END_IF;
END_IF;
ELSE
"DB2028_TEMP".TOF[#for_i].ET := "DB2028_TEMP".TOF[#for_i].SP;
END_IF;
END_FOR;
END_IF;
END_REGION GESTION_DISPOSITIVO_TOF
// ==========================================================================================================
REGION GESTION_DISPOSITIVO_TONR
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TONR";
IF #t_N_MAX >= 1 THEN
FOR #for_i := 1 TO #t_N_MAX DO
IF "DB2028_TEMP".TONR[#for_i].RESET THEN
"DB2028_TEMP".TONR[#for_i].ET := 0;
"DB2028_TEMP".TONR[#for_i].Q := FALSE;
"DB2028_TEMP".TONR[#for_i].RESET := FALSE;
END_IF;
IF "DB2028_TEMP".TONR[#for_i].IN AND
"DB2028_TEMP".TONR[#for_i].ET < "DB2028_TEMP".TONR[#for_i].SP AND
#Pulso1seg
THEN
"DB2028_TEMP".TONR[#for_i].ET += 1;
"DB2028_TEMP".TONR[#for_i].Q := FALSE;
END_IF;
IF "DB2028_TEMP".TONR[#for_i].ET >= "DB2028_TEMP".TONR[#for_i].SP THEN
"DB2028_TEMP".TONR[#for_i].Q := TRUE;
END_IF;
"DB2028_TEMP".TONR[#for_i].RT := "DB2028_TEMP".TONR[#for_i].SP - "DB2028_TEMP".TONR[#for_i].ET;
END_FOR;
END_IF;
END_REGION GESTION_DISPOSITIVO_TONR
// ==========================================================================================================
REGION GESTION_DISPOSITIVO_TP
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TP";
IF #t_N_MAX >= 1 THEN
FOR #for_i := 1 TO #t_N_MAX DO
// =============================================================================
// Detección de flanco
IF "DB2028_TEMP".TP[#for_i].IN AND NOT "DB2028_TEMP".TP[#for_i].oldIN THEN
"DB2028_TEMP".TP[#for_i].Q := TRUE;
END_IF;
// Actualizo valor de memoria
"DB2028_TEMP".TP[#for_i].oldIN := "DB2028_TEMP".TP[#for_i].IN;
// =============================================================================
// Reinicio memoria de activación cuando piedo salida y cuento tiempo
IF NOT "DB2028_TEMP".TP[#for_i].IN THEN
"DB2028_TEMP".TP[#for_i].oldIN := FALSE;
END_IF;
// =============================================================================
// Incremento de tiempo
IF "DB2028_TEMP".TP[#for_i].Q
AND
"DB2028_TEMP".TP[#for_i].ET < "DB2028_TEMP".TP[#for_i].PT
AND
#Pulso1seg
THEN
"DB2028_TEMP".TP[#for_i].ET += 1;
END_IF;
// =============================================================================
// Desactivo salida cuando cumplo tiempo de retardo de salida
IF "DB2028_TEMP".TP[#for_i].ET >= "DB2028_TEMP".TP[#for_i].PT THEN
"DB2028_TEMP".TP[#for_i].Q := FALSE;
"DB2028_TEMP".TP[#for_i].ET := 0;
END_IF;
END_FOR;
END_IF;
END_REGION GESTION_DISPOSITIVO_TP
END_FUNCTION
@@ -0,0 +1,160 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_qi";
S7_BlockNumber := "2037";
S7_BlockTitle := "MLC_3wJ";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2037_ZC_MEDIA" : Void
VAR_INPUT
{ S7_MLC := "MLC_344" }
Pulso1seg : Bool;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_NumeroDatos : LReal;
END_VAR
{ S7_Language := "SCL" }
NETWORK
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2022 - 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 Media.
Las tareas que realiza son las siguientes:
- Gestionar la activacion y calculo de la media.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2037_MEDIA` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 01.02.2022 | (ABH) | Primera version. |
| 00.00.02 | 18.09.2023 | (ABH) | Se añade valor maximo y valor minimo del valor instanteneo de la media cuando esta iniciada la media. |
| 01.00.00 | 06.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_MEDIA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_MEDIA";
END_REGION COMPROBACION_N_MAX
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
FOR #for_i := 1 TO #t_N_MAX DO
// ==========================================================================================================
REGION REINICIALIZACION_DE_DATOS
// Reset temporizador interno si no esta habilitada la media
IF NOT "DB2037_MEDIA".MEDIA[#for_i].IniciarMedia THEN
"DB2037_MEDIA".MEDIA[#for_i].auxTiempo := 0;
END_IF;
// Reinicializacion de datos al inicializar
IF "DB2037_MEDIA".MEDIA[#for_i].Inicializar AND NOT "DB2037_MEDIA".MEDIA[#for_i].auxInicializar THEN
"DB2037_MEDIA".MEDIA[#for_i].auxAcumulado := 0.0;
"DB2037_MEDIA".MEDIA[#for_i].auxIndice := 0;
"DB2037_MEDIA".MEDIA[#for_i].auxTiempo := 0;
"DB2037_MEDIA".MEDIA[#for_i].ValorMinimo := 9999999.0;
"DB2037_MEDIA".MEDIA[#for_i].ValorMaximo := -9999999.0;
END_IF;
// Estado anterior
"DB2037_MEDIA".MEDIA[#for_i].auxInicializar := "DB2037_MEDIA".MEDIA[#for_i].Inicializar;
END_REGION REINICIALIZACION_DE_DATOS
// ==========================================================================================================
REGION TOMA_DE_VALORES
IF "DB2037_MEDIA".MEDIA[#for_i].IniciarMedia AND #Pulso1seg THEN
// Incrementamos el indice de tiempo
"DB2037_MEDIA".MEDIA[#for_i].auxTiempo += 1;
// En caso de superar el tiempo de muestreo, incrementamos el acumulado, reseteamos el tiempo y aumentamos el indice
IF "DB2037_MEDIA".MEDIA[#for_i].auxTiempo >= "DB2037_MEDIA".MEDIA[#for_i].TiempoMuestreo THEN
"DB2037_MEDIA".MEDIA[#for_i].auxAcumulado := "DB2037_MEDIA".MEDIA[#for_i].ValorActual + "DB2037_MEDIA".MEDIA[#for_i].auxAcumulado;
"DB2037_MEDIA".MEDIA[#for_i].auxIndice += 1;
"DB2037_MEDIA".MEDIA[#for_i].auxTiempo := 0;
END_IF;
// Guardamos el valor minimo alcanzado
IF "DB2037_MEDIA".MEDIA[#for_i].ValorActual < "DB2037_MEDIA".MEDIA[#for_i].ValorMinimo THEN
"DB2037_MEDIA".MEDIA[#for_i].ValorMinimo := "DB2037_MEDIA".MEDIA[#for_i].ValorActual;
END_IF;
// Guardamos el valor maximo alcanzado
IF "DB2037_MEDIA".MEDIA[#for_i].ValorActual > "DB2037_MEDIA".MEDIA[#for_i].ValorMaximo THEN
"DB2037_MEDIA".MEDIA[#for_i].ValorMaximo := "DB2037_MEDIA".MEDIA[#for_i].ValorActual;
END_IF;
END_IF;
// Conversion de indice a REAL para calculos
#t_NumeroDatos := DINT_TO_LREAL("DB2037_MEDIA".MEDIA[#for_i].auxIndice);
END_REGION TOMA_DE_VALORES
// ==========================================================================================================
REGION CALCULO_MEDIA
IF "DB2037_MEDIA".MEDIA[#for_i].auxIndice > 0 THEN
"DB2037_MEDIA".MEDIA[#for_i].Media := "DB2037_MEDIA".MEDIA[#for_i].auxAcumulado / #t_NumeroDatos;
ELSE
"DB2037_MEDIA".MEDIA[#for_i].Media := 0.0;
END_IF;
END_REGION CALCULO_MEDIA
END_FOR;
END_REGION GESTION_DISPOSITIVO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_3wJ
es-ES: FC2037_ZC_MEDIA
- id: MLC_qi
es-ES: Funcion para gestion medias
- id: MLC_344
es-ES: Pulso de 1 segundo
@@ -1,156 +0,0 @@
FUNCTION "FC2037_ZC_MEDIA" : Void
TITLE = FC2037_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_TEMP
for_i : Int;
t_N_MAX : Int;
t_NumeroDatos : LReal;
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 |
---
> **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 | `DB2037_MEDIA` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 01.02.2022 | (ABH) | Primera version. |
| 00.00.02 | 18.09.2023 | (ABH) | Se añade valor maximo y valor minimo del valor instanteneo de la media cuando esta iniciada la media. |
| 01.00.00 | 06.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_MEDIA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_MEDIA";
END_REGION COMPROBACION_N_MAX
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
FOR #for_i := 1 TO #t_N_MAX DO
// ==========================================================================================================
REGION REINICIALIZACION_DE_DATOS
// Reset temporizador interno si no esta habilitada la media
IF NOT "DB2037_MEDIA".MEDIA[#for_i].IniciarMedia THEN
"DB2037_MEDIA".MEDIA[#for_i].auxTiempo := 0;
END_IF;
// Reinicializacion de datos al inicializar
IF "DB2037_MEDIA".MEDIA[#for_i].Inicializar AND NOT "DB2037_MEDIA".MEDIA[#for_i].auxInicializar THEN
"DB2037_MEDIA".MEDIA[#for_i].auxAcumulado := 0.0;
"DB2037_MEDIA".MEDIA[#for_i].auxIndice := 0;
"DB2037_MEDIA".MEDIA[#for_i].auxTiempo := 0;
"DB2037_MEDIA".MEDIA[#for_i].ValorMinimo := 9999999.0;
"DB2037_MEDIA".MEDIA[#for_i].ValorMaximo := -9999999.0;
END_IF;
// Estado anterior
"DB2037_MEDIA".MEDIA[#for_i].auxInicializar := "DB2037_MEDIA".MEDIA[#for_i].Inicializar;
END_REGION REINICIALIZACION_DE_DATOS
// ==========================================================================================================
REGION TOMA_DE_VALORES
IF "DB2037_MEDIA".MEDIA[#for_i].IniciarMedia AND #Pulso1seg THEN
// Incrementamos el indice de tiempo
"DB2037_MEDIA".MEDIA[#for_i].auxTiempo += 1;
// En caso de superar el tiempo de muestreo, incrementamos el acumulado, reseteamos el tiempo y aumentamos el indice
IF "DB2037_MEDIA".MEDIA[#for_i].auxTiempo >= "DB2037_MEDIA".MEDIA[#for_i].TiempoMuestreo THEN
"DB2037_MEDIA".MEDIA[#for_i].auxAcumulado := "DB2037_MEDIA".MEDIA[#for_i].ValorActual + "DB2037_MEDIA".MEDIA[#for_i].auxAcumulado;
"DB2037_MEDIA".MEDIA[#for_i].auxIndice += 1;
"DB2037_MEDIA".MEDIA[#for_i].auxTiempo := 0;
END_IF;
// Guardamos el valor minimo alcanzado
IF "DB2037_MEDIA".MEDIA[#for_i].ValorActual < "DB2037_MEDIA".MEDIA[#for_i].ValorMinimo THEN
"DB2037_MEDIA".MEDIA[#for_i].ValorMinimo := "DB2037_MEDIA".MEDIA[#for_i].ValorActual;
END_IF;
// Guardamos el valor maximo alcanzado
IF "DB2037_MEDIA".MEDIA[#for_i].ValorActual > "DB2037_MEDIA".MEDIA[#for_i].ValorMaximo THEN
"DB2037_MEDIA".MEDIA[#for_i].ValorMaximo := "DB2037_MEDIA".MEDIA[#for_i].ValorActual;
END_IF;
END_IF;
// Conversion de indice a REAL para calculos
#t_NumeroDatos := DINT_TO_LREAL("DB2037_MEDIA".MEDIA[#for_i].auxIndice);
END_REGION TOMA_DE_VALORES
// ==========================================================================================================
REGION CALCULO_MEDIA
IF "DB2037_MEDIA".MEDIA[#for_i].auxIndice > 0 THEN
"DB2037_MEDIA".MEDIA[#for_i].Media := "DB2037_MEDIA".MEDIA[#for_i].auxAcumulado / #t_NumeroDatos;
ELSE
"DB2037_MEDIA".MEDIA[#for_i].Media := 0.0;
END_IF;
END_REGION CALCULO_MEDIA
END_FOR;
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,151 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_Th";
S7_BlockNumber := "2038";
S7_BlockTitle := "MLC_4Kw";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2038_ZC_MEDIA_VOL" : Void
VAR_INPUT
{ S7_MLC := "MLC_Ne" }
Pulso1seg : Bool;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_NumeroDatos : LReal;
END_VAR
{ S7_Language := "SCL" }
NETWORK
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2024 - 2026
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **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 |
|----------|------------|---------|---------|
| 00.00.01 | 01.02.2024 | (ABH) | Primera version. |
| 00.00.02 | 16.02.2024 | (ABH) | Cambio de tipo de dato auxVolumen de REAL a LREAL. |
| 01.00.00 | 06.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay dispositivo declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_MEDIA_VOLUMEN" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_MEDIA_VOLUMEN";
END_REGION COMPROBACION_N_MAX
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
FOR #for_i := 1 TO #t_N_MAX DO
// ==========================================================================================================
REGION REINICIALIZACION_DE_DATOS
// Reset temporizador interno si no esta habilitada la media
IF NOT "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].IniciarMedia THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo := 0;
END_IF;
// Reinicializacion de datos al inicializar
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Inicializar AND NOT "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxInicializar THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxAcumulado :=
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxVolumen :=
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxValorMedia :=
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Media := 0.0;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo := 0;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMinimo := 9999999.0;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMaximo := -9999999.0;
END_IF;
// Estado anterior
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxInicializar := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Inicializar;
END_REGION REINICIALIZACION_DE_DATOS
// ==========================================================================================================
REGION TOMA_DE_VALORES
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].IniciarMedia AND #Pulso1seg THEN
// Incrementamos el indice de tiempo
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo += 1;
// En caso de superar el tiempo de muestreo, incrementamos el acumulado, reseteamos el tiempo y aumentamos el indice
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo >= "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].TiempoMuestreo THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxValorMedia := ("DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Volumen - "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxVolumen) * "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxVolumen := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Volumen;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxAcumulado := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxValorMedia + "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxAcumulado;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Media := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxAcumulado / "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Volumen;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo := 0;
END_IF;
// Guardamos el valor minimo alcanzado
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual < "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMinimo THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMinimo := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual;
END_IF;
// Guardamos el valor maximo alcanzado
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual > "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMaximo THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMaximo := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual;
END_IF;
END_IF;
END_REGION TOMA_DE_VALORES
END_FOR;
END_REGION GESTION_DISPOSITIVO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_4Kw
es-ES: FC2038_ZC_MEDIA_VOL
- id: MLC_Th
es-ES: Funcion para gestion de media teniendo en cuenta el volumen/caudal.
- id: MLC_Ne
es-ES: Pulso de 1 segundo
@@ -1,147 +0,0 @@
FUNCTION "FC2038_ZC_MEDIA_VOL" : Void
TITLE = FC2038_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_TEMP
for_i : Int;
t_N_MAX : Int;
t_NumeroDatos : LReal;
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2024 - 2026
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **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 |
|----------|------------|---------|---------|
| 00.00.01 | 01.02.2024 | (ABH) | Primera version. |
| 00.00.02 | 16.02.2024 | (ABH) | Cambio de tipo de dato auxVolumen de REAL a LREAL. |
| 01.00.00 | 06.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay dispositivo declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_MEDIA_VOLUMEN" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_MEDIA_VOLUMEN";
END_REGION COMPROBACION_N_MAX
// ==========================================================================================================
REGION GESTION_DISPOSITIVO
FOR #for_i := 1 TO #t_N_MAX DO
// ==========================================================================================================
REGION REINICIALIZACION_DE_DATOS
// Reset temporizador interno si no esta habilitada la media
IF NOT "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].IniciarMedia THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo := 0;
END_IF;
// Reinicializacion de datos al inicializar
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Inicializar AND NOT "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxInicializar THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxAcumulado :=
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxVolumen :=
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxValorMedia :=
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Media := 0.0;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo := 0;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMinimo := 9999999.0;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMaximo := -9999999.0;
END_IF;
// Estado anterior
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxInicializar := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Inicializar;
END_REGION REINICIALIZACION_DE_DATOS
// ==========================================================================================================
REGION TOMA_DE_VALORES
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].IniciarMedia AND #Pulso1seg THEN
// Incrementamos el indice de tiempo
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo += 1;
// En caso de superar el tiempo de muestreo, incrementamos el acumulado, reseteamos el tiempo y aumentamos el indice
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo >= "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].TiempoMuestreo THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxValorMedia := ("DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Volumen - "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxVolumen) * "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxVolumen := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Volumen;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxAcumulado := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxValorMedia + "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxAcumulado;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Media := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxAcumulado / "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].Volumen;
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].auxTiempo := 0;
END_IF;
// Guardamos el valor minimo alcanzado
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual < "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMinimo THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMinimo := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual;
END_IF;
// Guardamos el valor maximo alcanzado
IF "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual > "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMaximo THEN
"DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorMaximo := "DB2038_MEDIA_VOL".MEDIA_VOL[#for_i].ValorActual;
END_IF;
END_IF;
END_REGION TOMA_DE_VALORES
END_FOR;
END_REGION GESTION_DISPOSITIVO
END_FUNCTION
@@ -0,0 +1,390 @@
{
S7_Author := "HCR";
S7_BlockNumber := "2041";
S7_BlockTitle := "MLC_562";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2041_ZC_TQ_AE" : Void
VAR_INPUT
Pulso1seg : Bool;
Arranque : Bool;
Ack : Bool;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Index : Int;
t_oldIndex : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2024 - 2026
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
Bloque de gestión de dispositivos `TQ Aire Estéril`. Realiza la lógica de control, supervisión, alarmas y trazabilidad de los tanques de aire estéril de la planta, así como el multiplexado de los dispositivos hacia los distintos clientes HMI/SCADA.
> **Restricciones:** Ninguna.
Acciones detalladas:
- Inicializa los índices de multiplexado a `0` en el arranque del PLC.
- Gestiona los límites del índice (`Index`) y el flag `MaxVisible` para cada cliente HMI/SCADA.
- Realiza la carga inicial del dispositivo seleccionado al detectar un cambio de índice.
- Sincroniza los datos de lectura del dispositivo hacia la estructura del multiplexado (`Mux[...]`).
- Sincroniza los datos de escritura desde el multiplexado hacia el dispositivo, marcando `ExisteCambio` cuando se modifica algún valor.
- Propaga los cambios realizados desde SCADA al resto de multiplexados que tengan seleccionado el mismo dispositivo.
- Resetea los flags `AlgunaAlarma` y `AlgunaEnManual` de las agrupaciones de alarma en cada ciclo.
- Gestiona los modos de trabajo `AutoMan` y la orden `Orden_Manual`.
- Gestiona las alarmas de configuración, presión baja y presión alta, con temporización de confirmación mediante `Pulso1seg`.
- Controla la válvula de ducha durante el ciclo CIP mediante lógica flip-flop temporizada.
- Controla la válvula de venteo, inhibida por las alarmas de presión.
- Controla la válvula de estéril de forma modulante en función de la presión de trabajo.
- Registra la trazabilidad de los cambios de modo Auto/Manual y de la activación/desactivación de la orden manual mediante `FC8_ZC_TRAZA_REGISTRO`.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | `DB2041_ZC_TQ_AE` |
| UDT | `ZC_DISP_TQ_AE` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2024 | (HCR) | Primera versión. |
| 00.00.02 | 28.01.2025 | (ABH) | Se añade estado AutoMan y orden manual. Se añade trazabilidad. Se añade multiplexado. |
| 00.00.03 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.04 | 24.04.2025 | (HCR) | Se añade gestión de idioma. |
| 00.00.05 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traducción. |
| 00.00.06 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza códigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. |
| 01.00.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2041_TQ_AE".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2041_TQ_AE".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2041_TQ_AE".NumDispositivos := "N_MAX_DISP_TQ_AE";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_TQ_AE" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TQ_AE";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
IF "DB2041_TQ_AE".TQ_AE[#for_i].Config_Habilitar THEN
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan THEN
"DB2041_TQ_AE".Agrup["DB2041_TQ_AE".TQ_AE[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual := FALSE;
END_IF;
END_REGION
REGION ALARMAS
IF "DB2041_TQ_AE".TQ_AE[#for_i].Config_HabilitarAlarmas THEN
// =============================================================================
// ALARMA DE CONFIGURACION
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion := TRUE;
IF ("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinAlarma < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo) AND
("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo) AND
("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxAlarma) THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion := FALSE;
END_IF;
// =============================================================================
// ALARMA MINIMA PRESION
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := FALSE;
IF ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinAlarma)
AND
NOT #Ack
THEN
IF ("DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointAlarmaMinPresion) AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Vaciado THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := TRUE;
ELSE
IF #Pulso1seg THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion += 1;
END_IF;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion := 0;
END_IF;
// =============================================================================
// ALARMA MAXIMA PRESION
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta := FALSE;
IF ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion > "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxAlarma)
THEN
IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointAlarmaMaxPresion THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta := TRUE;
ELSE
IF #Pulso1seg THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion += 1;
END_IF;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion := 0;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := FALSE;
END_IF;
END_REGION
REGION VALVULA_DUCHA
IF "DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP AND
("DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlipDucha > 0
OR
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlopDucha > 0)
THEN
IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlanco = FALSE THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := TRUE;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlanco := TRUE;
END_IF;
// =============================================================================
// GESTION TIEMPO FLIP FLOP
IF #Pulso1seg THEN
// =============================================================================
// CUENTA FLIP
IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo = TRUE THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip += 1;
END_IF;
// =============================================================================
// CUENTA FLOP
IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo = TRUE THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop += 1;
END_IF;
// =============================================================================
// GESTION LOGICA Y SALIDAS FLIP
IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlipDucha THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip := FALSE;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := TRUE;
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip := 0;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo := false;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo := true;
END_IF;
// =============================================================================
// GESTION LOGICA Y SALIDAS FLOP
IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlopDucha THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip := TRUE;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := FALSE;
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop := 0;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo := true;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo := false;
END_IF;
END_IF;
END_IF;
// =============================================================================
// GESTION LOGICA SALIDA DUCHA
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaDucha :=
(
NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan
AND
(
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion
OR
("DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip)
)
)
OR
("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual);
END_REGION
REGION VALVULA_VENTEO
// =============================================================================
// GESTION LOGICA SALIDA VENTEO
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaVenteo :=
(
(NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion)
OR
("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual)
)
AND
NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja;
END_REGION
REGION VALVULA_ESTERIL
// =============================================================================
// GESTION LOGICA SALIDA ESTERIL
IF (NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion)
OR
("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual)
THEN
IF NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja THEN
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion <= "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := TRUE;
END_IF;
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
END_IF;
END_REGION
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_20_DISP_TQ_AE",
User := "DB1_SYS".Usuario.Actual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
CodInt_2 := #for_i,
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 "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_20_DISP_TQ_AE",
User := "DB1_SYS".Usuario.Actual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_20_DISP_TQ_AE",
User := "DB1_SYS".Usuario.Actual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_20_DISP_TQ_AE",
User := "DB1_SYS".Usuario.Actual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual := "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan := "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan;
ELSE
// =============================================================================
// INICIALIZACIÓN DE SALIDAS
"DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan :=
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual :=
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion :=
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP :=
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Vaciado :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja :=
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaDucha :=
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril :=
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaVenteo := FALSE;
END_IF;
END_FOR;
END_REGION
END_NETWORK
END_FUNCTION
@@ -0,0 +1,3 @@
MultiLingualTexts:
- id: MLC_562
es-ES: FC2041_ZC_TQ_AE
@@ -1,387 +0,0 @@
FUNCTION "FC2041_ZC_TQ_AE" : Void
TITLE = FC2041_ZC_TQ_AE
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : HCR
FAMILY : ZeusControl
VERSION : 1.0
VAR_INPUT
Pulso1seg : Bool;
Arranque : Bool;
Ack : Bool;
END_VAR
VAR_TEMP
for_i : Int;
t_N_MAX : Int;
t_Index : Int;
t_oldIndex : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
BEGIN
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2024 - 2026
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
Bloque de gestión de dispositivos `TQ Aire Estéril`. Realiza la lógica de control, supervisión, alarmas y trazabilidad de los tanques de aire estéril de la planta, así como el multiplexado de los dispositivos hacia los distintos clientes HMI/SCADA.
> **Restricciones:** Ninguna.
Acciones detalladas:
- Inicializa los índices de multiplexado a `0` en el arranque del PLC.
- Gestiona los límites del índice (`Index`) y el flag `MaxVisible` para cada cliente HMI/SCADA.
- Realiza la carga inicial del dispositivo seleccionado al detectar un cambio de índice.
- Sincroniza los datos de lectura del dispositivo hacia la estructura del multiplexado (`Mux[...]`).
- Sincroniza los datos de escritura desde el multiplexado hacia el dispositivo, marcando `ExisteCambio` cuando se modifica algún valor.
- Propaga los cambios realizados desde SCADA al resto de multiplexados que tengan seleccionado el mismo dispositivo.
- Resetea los flags `AlgunaAlarma` y `AlgunaEnManual` de las agrupaciones de alarma en cada ciclo.
- Gestiona los modos de trabajo `AutoMan` y la orden `Orden_Manual`.
- Gestiona las alarmas de configuración, presión baja y presión alta, con temporización de confirmación mediante `Pulso1seg`.
- Controla la válvula de ducha durante el ciclo CIP mediante lógica flip-flop temporizada.
- Controla la válvula de venteo, inhibida por las alarmas de presión.
- Controla la válvula de estéril de forma modulante en función de la presión de trabajo.
- Registra la trazabilidad de los cambios de modo Auto/Manual y de la activación/desactivación de la orden manual mediante `FC8_ZC_TRAZA_REGISTRO`.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | `DB2041_ZC_TQ_AE` |
| UDT | `ZC_DISP_TQ_AE` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 15.10.2024 | (HCR) | Primera versión. |
| 00.00.02 | 28.01.2025 | (ABH) | Se añade estado AutoMan y orden manual. Se añade trazabilidad. Se añade multiplexado. |
| 00.00.03 | 26.03.2025 | (ABH) | Se añade variable grupo de alarma para separar los nuevos avisos por cuadro. |
| 00.00.04 | 24.04.2025 | (HCR) | Se añade gestión de idioma. |
| 00.00.05 | 11.09.2025 | (ABH) | Se eliminan las constantes de idioma dentro del FC. Se utilizan constantes globales para facilitar su traducción. |
| 00.00.06 | 17.12.2025 | (ABH) | Se eliminan los idiomas. Se utiliza códigos para trazabilidad. Se elimina entrada "Idioma". Se elimina nombre de UDT. |
| 01.00.00 | 08.07.2026 | (ABH) | Se extrae el multiplexado a FC independiente. |
*)
END_REGION DESCRIPCION
REGION LOGICA_DEL_DISPOSITIVO
// =============================================================================
// Resetear todas las alarmas para que se vuelvan a generar
FOR #for_i := 0 TO "N_MAX_DISP_AGRUP" DO
"DB2041_TQ_AE".Agrup[#for_i].AlgunaAlarma := FALSE;
"DB2041_TQ_AE".Agrup[#for_i].AlgunaEnManual := FALSE;
END_FOR;
// Numero maximo de dispositivos para limitar en HMI/Scada
"DB2041_TQ_AE".NumDispositivos := "N_MAX_DISP_TQ_AE";
REGION COMPROBACION_N_MAX
// Si no hay dispositivos declarados, salimos del bloque inmediatamente
IF "N_MAX_DISP_TQ_AE" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_DISP_TQ_AE";
END_REGION COMPROBACION_N_MAX
FOR #for_i := 1 TO #t_N_MAX DO
IF "DB2041_TQ_AE".TQ_AE[#for_i].Config_Habilitar THEN
REGION MODOS_DE_TRABAJO
// =============================================================================
// MODO MANUAL
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan THEN
"DB2041_TQ_AE".Agrup["DB2041_TQ_AE".TQ_AE[#for_i].Config_GrupoAlarma].AlgunaEnManual := TRUE;
END_IF;
// =============================================================================
// MODO AUTO
IF NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual := FALSE;
END_IF;
END_REGION
REGION ALARMAS
IF "DB2041_TQ_AE".TQ_AE[#for_i].Config_HabilitarAlarmas THEN
// =============================================================================
// ALARMA DE CONFIGURACION
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion := TRUE;
IF ("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinAlarma < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo) AND
("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo) AND
("DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxAlarma) THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion := FALSE;
END_IF;
// =============================================================================
// ALARMA MINIMA PRESION
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := FALSE;
IF ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion < "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinAlarma)
AND
NOT #Ack
THEN
IF ("DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointAlarmaMinPresion) AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Vaciado THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := TRUE;
ELSE
IF #Pulso1seg THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion += 1;
END_IF;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMinPresion := 0;
END_IF;
// =============================================================================
// ALARMA MAXIMA PRESION
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta := FALSE;
IF ("DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion > "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxAlarma)
THEN
IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointAlarmaMaxPresion THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta := TRUE;
ELSE
IF #Pulso1seg THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion += 1;
END_IF;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_AlarmaMaxPresion := 0;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja := FALSE;
END_IF;
END_REGION
REGION VALVULA_DUCHA
IF "DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP AND
("DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlipDucha > 0
OR
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlopDucha > 0)
THEN
IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlanco = FALSE THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := TRUE;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlanco := TRUE;
END_IF;
// =============================================================================
// GESTION TIEMPO FLIP FLOP
IF #Pulso1seg THEN
// =============================================================================
// CUENTA FLIP
IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo = TRUE THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip += 1;
END_IF;
// =============================================================================
// CUENTA FLOP
IF "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo = TRUE THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop += 1;
END_IF;
// =============================================================================
// GESTION LOGICA Y SALIDAS FLIP
IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlipDucha THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip := FALSE;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := TRUE;
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlip := 0;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo := false;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo := true;
END_IF;
// =============================================================================
// GESTION LOGICA Y SALIDAS FLOP
IF "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop >= "DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_SetPointFlopDucha THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip := TRUE;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlop := FALSE;
"DB2041_TQ_AE".TQ_AE[#for_i].Tiempos_DuchaTiempoFlop := 0;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlipActivo := true;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaFlopActivo := false;
END_IF;
END_IF;
END_IF;
// =============================================================================
// GESTION LOGICA SALIDA DUCHA
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaDucha :=
(
NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan
AND
(
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion
OR
("DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_DuchaSalidaFlip)
)
)
OR
("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual);
END_REGION
REGION VALVULA_VENTEO
// =============================================================================
// GESTION LOGICA SALIDA VENTEO
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaVenteo :=
(
(NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion)
OR
("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual)
)
AND
NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja;
END_REGION
REGION VALVULA_ESTERIL
// =============================================================================
// GESTION LOGICA SALIDA ESTERIL
IF (NOT "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion)
OR
("DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual)
THEN
IF NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja THEN
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion <= "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMinTrabajo THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := TRUE;
END_IF;
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_ValorPresion >= "DB2041_TQ_AE".TQ_AE[#for_i].Config_PresionMaxTrabajo THEN
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
END_IF;
ELSE
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril := FALSE;
END_IF;
END_REGION
REGION TRAZABILIDAD_MANUALIZACION
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_20_DISP_TQ_AE",
User := "DB1_SYS".Usuario.Actual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
CodInt_2 := #for_i,
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 "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_20_DISP_TQ_AE",
User := "DB1_SYS".Usuario.Actual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_AUTO",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_20_DISP_TQ_AE",
User := "DB1_SYS".Usuario.Actual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_ON",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
IF "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan AND NOT "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual AND "DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual THEN
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_20_DISP_TQ_AE",
User := "DB1_SYS".Usuario.Actual,
CodInt_1 := "TRZ_SYS_DISP_TIPO_TQ_AE",
CodInt_2 := #for_i,
CodInt_3 := "TRZ_SYS_DISP_ACC_MAN_OFF",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
END_IF;
END_REGION
// =============================================================================
// Estado anterior manual/automatico
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldOrdenManual := "DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual;
"DB2041_TQ_AE".TQ_AE[#for_i].Aux_oldAutoMan := "DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan;
ELSE
// =============================================================================
// INICIALIZACIÓN DE SALIDAS
"DB2041_TQ_AE".TQ_AE[#for_i].Estado_AutoMan :=
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Manual :=
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Produccion :=
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_CIP :=
"DB2041_TQ_AE".TQ_AE[#for_i].Orden_Vaciado :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_Configuracion :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionAlta :=
"DB2041_TQ_AE".TQ_AE[#for_i].Alarma_PresionBaja :=
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaDucha :=
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaEsteril :=
"DB2041_TQ_AE".TQ_AE[#for_i].Salida_ValvulaVenteo := FALSE;
END_IF;
END_FOR;
END_REGION
END_FUNCTION
@@ -0,0 +1,204 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_339";
S7_BlockNumber := "2926";
S7_BlockTitle := "MLC_4ir";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2926_ZC_FF_INDV" : Void
VAR_INPUT
{ S7_MLC := "MLC_4j6" }
Pulso1Seg : Bool;
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
{ S7_Language := "SCL" }
NETWORK
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_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_4ir
es-ES: FC2926_ZC_FF
- id: MLC_339
es-ES: Funcion para gestion de flip flop
- id: MLC_4j6
es-ES: Pulso de 1 segundo
@@ -1,201 +0,0 @@
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,200 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4dQ";
S7_BlockNumber := "2927";
S7_BlockTitle := "MLC_3Pj";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2927_ZC_FF_SEC_INDV" : Void
VAR_INPUT
{ S7_MLC := "MLC_W4" }
Pulso1Seg : Bool;
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
{ S7_Language := "SCL" }
NETWORK
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_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_3Pj
es-ES: FC2927_FF_SEC
- id: MLC_4dQ
es-ES: Funcion para gestion de Flip Flop secuencial
- id: MLC_W4
es-ES: Pulso 1 segundo
@@ -1,198 +0,0 @@
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,81 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_45T";
S7_BlockNumber := "2928";
S7_BlockTitle := "MLC_3UK";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2928_ZC_TON_INDV" : Void
VAR_INPUT
Pulso1seg : Bool;
END_VAR
VAR_IN_OUT
TON : _.UDT_ZC_DISP_TON;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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_NETWORK
END_FUNCTION
@@ -0,0 +1,5 @@
MultiLingualTexts:
- id: MLC_3UK
es-ES: FC2928_TON
- id: MLC_45T
es-ES: Funcion para gestion temporizador TON (Base 1 seg)
@@ -1,78 +0,0 @@
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,86 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_Nd";
S7_BlockNumber := "2929";
S7_BlockTitle := "MLC_3mr";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2929_ZC_TOF_INDV" : Void
VAR_INPUT
Pulso1seg : Bool;
END_VAR
VAR_IN_OUT
TOF : _.UDT_ZC_DISP_TOF;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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_NETWORK
END_FUNCTION
@@ -0,0 +1,5 @@
MultiLingualTexts:
- id: MLC_3mr
es-ES: FC2929_TOF
- id: MLC_Nd
es-ES: Funcion para gestion temporizador TOF (Base 1 seg)
@@ -1,83 +0,0 @@
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,90 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_3Vs";
S7_BlockNumber := "2930";
S7_BlockTitle := "MLC_3r3";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2930_ZC_TONR_INDV" : Void
VAR_INPUT
Pulso1seg : Bool;
END_VAR
VAR_IN_OUT
TONR : _.UDT_ZC_DISP_TONR;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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_NETWORK
END_FUNCTION
@@ -0,0 +1,5 @@
MultiLingualTexts:
- id: MLC_3r3
es-ES: FC2930_TONR
- id: MLC_3Vs
es-ES: Funcion para gestion temporizador TONR (Base 1 seg)
@@ -1,87 +0,0 @@
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,101 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4Eb";
S7_BlockNumber := "2931";
S7_BlockTitle := "MLC_jB";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2931_ZC_TP_INDV" : Void
VAR_INPUT
Pulso1Seg : Bool;
END_VAR
VAR_IN_OUT
TP : _.UDT_ZC_DISP_TP;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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_NETWORK
END_FUNCTION
@@ -0,0 +1,5 @@
MultiLingualTexts:
- id: MLC_jB
es-ES: FC2931_TP
- id: MLC_4Eb
es-ES: Funcion para gestion temporizador TP (Base 1 seg)
@@ -1,98 +0,0 @@
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,321 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_ia";
S7_BlockNumber := "2935";
S7_BlockTitle := "MLC_48Y";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2935_ZC_PID_INDV" : Void
VAR_INPUT
{ S7_MLC := "MLC_3hx" }
PV : Real;
{ S7_MLC := "MLC_58p" }
DispManAuto : Bool;
{ S7_MLC := "MLC_3ta" }
DispValorActual : Real;
{ S7_MLC := "MLC_3PL" }
Ack : Bool;
{ S7_MLC := "MLC_Pm" }
Fecha : DTL;
{ S7_MLC := "MLC_qt" }
Usuario : String;
{ S7_MLC := "MLC_33T" }
NumeroPID : Int;
{ S7_MLC := "MLC_3tf" }
RangoSuperiorEscalado : Real;
{ S7_MLC := "MLC_55J" }
RangoInferiorEscalado : Real;
{ S7_MLC := "MLC_49H" }
UsuarioActual : String;
{ S7_MLC := "MLC_3nL" }
FechaHoraActual : DTL;
END_VAR
VAR_IN_OUT
PID_CompactConfig : PID_CompactConfig;
PID_CompactControlParams : PID_CompactControlParams;
END_VAR
VAR_TEMP
t_Norm_X : LReal;
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 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".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_NETWORK
END_FUNCTION
@@ -0,0 +1,27 @@
MultiLingualTexts:
- id: MLC_48Y
es-ES: FC2935_ZC_PID_INDV
- id: MLC_ia
es-ES: Funcion de gestion dispositivo PID
- id: MLC_3hx
es-ES: Valor de proceso
- id: MLC_58p
es-ES: Dispositivo Manual - Auto
- id: MLC_3ta
es-ES: Dispositivo valor actual
- id: MLC_3PL
es-ES: Acuse general
- id: MLC_Pm
es-ES: Fecha y hora actual
- id: MLC_qt
es-ES: Usuario actual
- id: MLC_33T
es-ES: Numero de PID
- id: MLC_3tf
es-ES: Rango superior de valor de proceso para escalado 0-100%
- id: MLC_55J
es-ES: Rango inferior de valor de proceso para escalado 0-100%
- id: MLC_49H
es-ES: Usuario actual
- id: MLC_3nL
es-ES: Fecha y hora actual
@@ -1,308 +0,0 @@
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".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,142 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4CR";
S7_BlockNumber := "2937";
S7_BlockTitle := "MLC_Z9";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2937_ZC_MEDIA_INDV" : Void
VAR_INPUT
{ S7_MLC := "MLC_3rq" }
Pulso1seg : Bool;
END_VAR
VAR_IN_OUT
{ S7_MLC := "MLC_RE" }
Media : _.UDT_ZC_DISP_MEDIA;
END_VAR
VAR_TEMP
t_NumeroDatos : LReal;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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_NETWORK
END_FUNCTION
@@ -0,0 +1,9 @@
MultiLingualTexts:
- id: MLC_Z9
es-ES: FC2937_ZC_MEDIA
- id: MLC_4CR
es-ES: Funcion para gestion medias
- id: MLC_3rq
es-ES: Pulso de 1 segundo
- id: MLC_RE
es-ES: Media
@@ -1,138 +0,0 @@
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,133 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_3xu";
S7_BlockNumber := "2938";
S7_BlockTitle := "MLC_3ci";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2938_ZC_MEDIA_VOL_INDV" : Void
VAR_INPUT
{ S7_MLC := "MLC_kv" }
Pulso1seg : Bool;
END_VAR
VAR_IN_OUT
{ S7_MLC := "MLC_nF" }
Media : _.UDT_ZC_DISP_MEDIA_VOLUMEN;
END_VAR
VAR_TEMP
t_NumeroDatos : LReal;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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_NETWORK
END_FUNCTION
@@ -0,0 +1,9 @@
MultiLingualTexts:
- id: MLC_3ci
es-ES: FC2938_ZC_MEDIA_VOL
- id: MLC_3xu
es-ES: Funcion para gestion de media teniendo en cuenta el volumen/caudal.
- id: MLC_kv
es-ES: Pulso de 1 segundo
- id: MLC_nF
es-ES: Media
@@ -1,129 +0,0 @@
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,627 @@
{
S7_Author := "HCR";
S7_BlockComment := "MLC_56n";
S7_BlockNumber := "2940";
S7_BlockTitle := "MLC_3Yu";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_IECCheck := "TRUE";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2940_ZC_TQ_INDV" : Void
VAR_INPUT
{ S7_MLC := "MLC_3JR" }
Pulso1seg : Bool;
{ S7_MLC := "MLC_4AZ" }
Arranque : Bool;
{ S7_MLC := "MLC_3nu" }
EstadoBocaHombre : Bool;
{ S7_MLC := "MLC_3qH" }
EstadoNivelBajo : Bool;
{ S7_MLC := "MLC_pu" }
EstadoNivelAlto : Bool;
{ S7_MLC := "MLC_Vz" }
EstadoComOK : Bool;
{ S7_MLC := "MLC_56U" }
ValorNivelPresion : Real;
{ S7_MLC := "MLC_54K" }
ValorTemperatura : Real;
{ S7_MLC := "MLC_sG" }
HisteresisAltoNivelAgitacion : Real;
{ S7_MLC := "MLC_4hG" }
HisteresisBajoNivelAgitacion : Real;
{ S7_MLC := "MLC_49y" }
HisteresisAltoNivelTemp : Real;
{ S7_MLC := "MLC_4tb" }
HisteresisBajoNivelTemp : Real;
{ S7_MLC := "MLC_57N" }
TiempoTanqueVacio : Int;
{ S7_MLC := "MLC_44t" }
TiempoTanqueLLeno : Int;
{ S7_MLC := "MLC_3AZ" }
HabilitarFrecAgitacion : Bool;
{ S7_MLC := "MLC_4LF" }
NumeroTanque : Int;
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
{ S7_Language := "SCL" }
NETWORK
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".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_NETWORK
END_FUNCTION
@@ -0,0 +1,37 @@
MultiLingualTexts:
- id: MLC_3Yu
es-ES: FC2940_ZC_TQ_INDV
- id: MLC_56n
es-ES: Funcion para gestion dispositivo tanque.
- id: MLC_3JR
es-ES: Pulso de 1 segundo
- id: MLC_4AZ
es-ES: Arranque
- id: MLC_3nu
es-ES: Estado boca de hombre
- id: MLC_3qH
es-ES: Estado nivel bajo
- id: MLC_pu
es-ES: Estado nivel alto
- id: MLC_Vz
es-ES: Estado comunicaciones Ok
- id: MLC_56U
es-ES: Valor de nivel / presion
- id: MLC_54K
es-ES: Valor temperatura
- id: MLC_sG
es-ES: Histeresis superior nivel agitacion
- id: MLC_4hG
es-ES: Histeresis inferior nivel agitacion
- id: MLC_49y
es-ES: Histeresis superior nivel temperatura
- id: MLC_4tb
es-ES: Histeresis inferior nivel temperatura
- id: MLC_57N
es-ES: Tiempo retardo tanque vacio
- id: MLC_44t
es-ES: Tiempo retardo tanque lleno
- id: MLC_3AZ
es-ES: Habilitar Frecuencia en Agitacion
- id: MLC_4LF
es-ES: Numero de tanque
@@ -1,608 +0,0 @@
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".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
@@ -0,0 +1,325 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_3ju";
S7_BlockNumber := "2800";
S7_BlockTitle := "MLC_Rx";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2800_ZC_ED_MUX" : Void
VAR_INPUT
{ S7_MLC := "MLC_3JR" }
Arranque : Bool;
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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
El bloque implementa la gestión de multiplexado estático para el dispositivo entrada digital, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_ED`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2000_ED.ED[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `ED[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2000_ED` |
| UDT | `ZC_DISP_ED`, `ZC_DISP_MUX_ED` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2800_ED_MUX".Mux.Scada[#for_mux].Index :=
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2800_ED_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2800_ED_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2800_ED_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_ED" THEN
"DB2800_ED_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_ED";
END_IF;
END_REGION LIMITES
// Traspaso de limites a variable temporal para evitar cambios no deseados
REGION INDICES
#t_Index := "DB2800_ED_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2800_ED_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice y almacenamos el estado original
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED := "DB2000_ED".ED[#t_Index];
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED := "DB2000_ED".ED[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Hmi_Estado := "DB2000_ED".ED[#t_Index].Hmi_Estado;
IF NOT "DB2000_ED".ED[#t_Index].Estado_AutoMan THEN
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
END_IF;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_Activado := "DB2000_ED".ED[#t_Index].Estado_Activado;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_EntradaCanal := "DB2000_ED".ED[#t_Index].Estado_EntradaCanal;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Aux_oldActivado := "DB2000_ED".ED[#t_Index].Aux_oldActivado;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Aux_oldAutoMan := "DB2000_ED".ED[#t_Index].Aux_oldAutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_TiempoAct := "DB2000_ED".ED[#t_Index].Tiempos_TiempoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_TiempoDes := "DB2000_ED".ED[#t_Index].Tiempos_TiempoDes;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_GrupoAlarma := "DB2000_ED".ED[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2000_ED".ED[#t_Index].Estado_AutoMan <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan THEN
// Cambio desde HMI
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan := "DB2000_ED".ED[#t_Index].Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan := "DB2000_ED".ED[#t_Index].Estado_AutoMan;
ELSIF "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2000_ED".ED[#t_Index].Estado_AutoMan := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2000_ED".ED[#t_Index].Orden_ActivarManual <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
ELSIF "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2000_ED".ED[#t_Index].Orden_ActivarManual := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaBit <> "DB2000_ED".ED[#t_Index].Config_DirEntradaBit)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaByte <> "DB2000_ED".ED[#t_Index].Config_DirEntradaByte)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_Habilitar <> "DB2000_ED".ED[#t_Index].Config_Habilitar)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarInvertir <> "DB2000_ED".ED[#t_Index].Config_HabilitarInvertir)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoAct <> "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoAct)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoDes <> "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoDes)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_TipoAcceso <> "DB2000_ED".ED[#t_Index].Config_TipoAcceso)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoAct <> "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoAct)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoDes <> "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoDes);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaBit <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaBit)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaByte <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaByte)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_Habilitar <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_Habilitar)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarInvertir <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarInvertir)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoAct <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoAct)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoDes <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoDes)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_TipoAcceso <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_TipoAcceso)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoAct <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoAct)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoDes <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoDes);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan := "DB2000_ED".ED[#t_Index].Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaBit := "DB2000_ED".ED[#t_Index].Config_DirEntradaBit;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaByte := "DB2000_ED".ED[#t_Index].Config_DirEntradaByte;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_Habilitar := "DB2000_ED".ED[#t_Index].Config_Habilitar;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarInvertir := "DB2000_ED".ED[#t_Index].Config_HabilitarInvertir;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoAct := "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoDes := "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoDes;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_TipoAcceso := "DB2000_ED".ED[#t_Index].Config_TipoAcceso;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoAct := "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoDes := "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoDes;
// Actualizamos el estado original para configuraciones
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan := "DB2000_ED".ED[#t_Index].Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaBit := "DB2000_ED".ED[#t_Index].Config_DirEntradaBit;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaByte := "DB2000_ED".ED[#t_Index].Config_DirEntradaByte;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_Habilitar := "DB2000_ED".ED[#t_Index].Config_Habilitar;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarInvertir := "DB2000_ED".ED[#t_Index].Config_HabilitarInvertir;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoAct := "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoDes := "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoDes;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_TipoAcceso := "DB2000_ED".ED[#t_Index].Config_TipoAcceso;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoAct := "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoDes := "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoDes;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2000_ED".ED[#t_Index].Estado_AutoMan := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan;
"DB2000_ED".ED[#t_Index].Orden_ActivarManual := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual;
"DB2000_ED".ED[#t_Index].Config_DirEntradaBit := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaBit;
"DB2000_ED".ED[#t_Index].Config_DirEntradaByte := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaByte;
"DB2000_ED".ED[#t_Index].Config_Habilitar := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_Habilitar;
"DB2000_ED".ED[#t_Index].Config_HabilitarInvertir := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarInvertir;
"DB2000_ED".ED[#t_Index].Config_HabilitarRetardoAct := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoAct;
"DB2000_ED".ED[#t_Index].Config_HabilitarRetardoDes := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoDes;
"DB2000_ED".ED[#t_Index].Config_TipoAcceso := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_TipoAcceso;
"DB2000_ED".ED[#t_Index].Tiempos_SPTiempoAct := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoAct;
"DB2000_ED".ED[#t_Index].Tiempos_SPTiempoDes := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoDes;
// Actualizamos el estado original para configuraciones
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaBit := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaBit;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaByte := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaByte;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_Habilitar := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_Habilitar;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarInvertir := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarInvertir;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoAct := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoDes := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoDes;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_TipoAcceso := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_TipoAcceso;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoAct := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoDes := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoDes;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la ED[0] que no se usa para escribir el index.
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED := "DB2000_ED".ED[0];
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED := "DB2000_ED".ED[0];
// Actualizacion de estado anterior
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2800_ED_MUX".Mux.Scada[#for_mux].Index :=
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_Rx
es-ES: FC2800_ZC_ED_MUX
- id: MLC_3ju
es-ES: Funcion de gestion multiplexado dispositivo Entrada digital
- id: MLC_3JR
es-ES: Primer arranque
@@ -1,321 +0,0 @@
FUNCTION "FC2800_ZC_ED_MUX" : Void
TITLE = FC2800_ZC_ED_MUX
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion multiplexado dispositivo Entrada digital
VAR_INPUT
Arranque : Bool; // Primer arranque
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
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
El bloque implementa la gestión de multiplexado estático para el dispositivo entrada digital, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_ED`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2000_ED.ED[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `ED[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2000_ED` |
| UDT | `ZC_DISP_ED`, `ZC_DISP_MUX_ED` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2800_ED_MUX".Mux.Scada[#for_mux].Index :=
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2800_ED_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2800_ED_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2800_ED_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_ED" THEN
"DB2800_ED_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_ED";
END_IF;
END_REGION LIMITES
// Traspaso de limites a variable temporal para evitar cambios no deseados
REGION INDICES
#t_Index := "DB2800_ED_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2800_ED_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice y almacenamos el estado original
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED := "DB2000_ED".ED[#t_Index];
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED := "DB2000_ED".ED[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Hmi_Estado := "DB2000_ED".ED[#t_Index].Hmi_Estado;
IF NOT "DB2000_ED".ED[#t_Index].Estado_AutoMan THEN
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
END_IF;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_Activado := "DB2000_ED".ED[#t_Index].Estado_Activado;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_EntradaCanal := "DB2000_ED".ED[#t_Index].Estado_EntradaCanal;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Aux_oldActivado := "DB2000_ED".ED[#t_Index].Aux_oldActivado;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Aux_oldAutoMan := "DB2000_ED".ED[#t_Index].Aux_oldAutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_TiempoAct := "DB2000_ED".ED[#t_Index].Tiempos_TiempoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_TiempoDes := "DB2000_ED".ED[#t_Index].Tiempos_TiempoDes;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_GrupoAlarma := "DB2000_ED".ED[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2000_ED".ED[#t_Index].Estado_AutoMan <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan THEN
// Cambio desde HMI
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan := "DB2000_ED".ED[#t_Index].Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan := "DB2000_ED".ED[#t_Index].Estado_AutoMan;
ELSIF "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2000_ED".ED[#t_Index].Estado_AutoMan := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2000_ED".ED[#t_Index].Orden_ActivarManual <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
ELSIF "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2000_ED".ED[#t_Index].Orden_ActivarManual := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaBit <> "DB2000_ED".ED[#t_Index].Config_DirEntradaBit)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaByte <> "DB2000_ED".ED[#t_Index].Config_DirEntradaByte)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_Habilitar <> "DB2000_ED".ED[#t_Index].Config_Habilitar)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarInvertir <> "DB2000_ED".ED[#t_Index].Config_HabilitarInvertir)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoAct <> "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoAct)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoDes <> "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoDes)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_TipoAcceso <> "DB2000_ED".ED[#t_Index].Config_TipoAcceso)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoAct <> "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoAct)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoDes <> "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoDes);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaBit <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaBit)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaByte <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaByte)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_Habilitar <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_Habilitar)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarInvertir <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarInvertir)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoAct <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoAct)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoDes <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoDes)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_TipoAcceso <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_TipoAcceso)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoAct <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoAct)
OR ("DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoDes <> "DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoDes);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan := "DB2000_ED".ED[#t_Index].Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaBit := "DB2000_ED".ED[#t_Index].Config_DirEntradaBit;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaByte := "DB2000_ED".ED[#t_Index].Config_DirEntradaByte;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_Habilitar := "DB2000_ED".ED[#t_Index].Config_Habilitar;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarInvertir := "DB2000_ED".ED[#t_Index].Config_HabilitarInvertir;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoAct := "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoDes := "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoDes;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_TipoAcceso := "DB2000_ED".ED[#t_Index].Config_TipoAcceso;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoAct := "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoDes := "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoDes;
// Actualizamos el estado original para configuraciones
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan := "DB2000_ED".ED[#t_Index].Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual := "DB2000_ED".ED[#t_Index].Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaBit := "DB2000_ED".ED[#t_Index].Config_DirEntradaBit;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaByte := "DB2000_ED".ED[#t_Index].Config_DirEntradaByte;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_Habilitar := "DB2000_ED".ED[#t_Index].Config_Habilitar;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarInvertir := "DB2000_ED".ED[#t_Index].Config_HabilitarInvertir;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoAct := "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoDes := "DB2000_ED".ED[#t_Index].Config_HabilitarRetardoDes;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_TipoAcceso := "DB2000_ED".ED[#t_Index].Config_TipoAcceso;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoAct := "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoDes := "DB2000_ED".ED[#t_Index].Tiempos_SPTiempoDes;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2000_ED".ED[#t_Index].Estado_AutoMan := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan;
"DB2000_ED".ED[#t_Index].Orden_ActivarManual := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual;
"DB2000_ED".ED[#t_Index].Config_DirEntradaBit := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaBit;
"DB2000_ED".ED[#t_Index].Config_DirEntradaByte := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaByte;
"DB2000_ED".ED[#t_Index].Config_Habilitar := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_Habilitar;
"DB2000_ED".ED[#t_Index].Config_HabilitarInvertir := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarInvertir;
"DB2000_ED".ED[#t_Index].Config_HabilitarRetardoAct := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoAct;
"DB2000_ED".ED[#t_Index].Config_HabilitarRetardoDes := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoDes;
"DB2000_ED".ED[#t_Index].Config_TipoAcceso := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_TipoAcceso;
"DB2000_ED".ED[#t_Index].Tiempos_SPTiempoAct := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoAct;
"DB2000_ED".ED[#t_Index].Tiempos_SPTiempoDes := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoDes;
// Actualizamos el estado original para configuraciones
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Estado_AutoMan := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Estado_AutoMan;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Orden_ActivarManual := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Orden_ActivarManual;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaBit := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaBit;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_DirEntradaByte := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_DirEntradaByte;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_Habilitar := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_Habilitar;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarInvertir := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarInvertir;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoAct := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_HabilitarRetardoDes := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_HabilitarRetardoDes;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Config_TipoAcceso := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Config_TipoAcceso;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoAct := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoAct;
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED.Tiempos_SPTiempoDes := "DB2800_ED_MUX".Mux.Scada[#for_mux].ED.Tiempos_SPTiempoDes;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la ED[0] que no se usa para escribir el index.
"DB2800_ED_MUX".Mux.Scada[#for_mux].ED := "DB2000_ED".ED[0];
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldED := "DB2000_ED".ED[0];
// Actualizacion de estado anterior
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2800_ED_MUX".Mux.Scada[#for_mux].Index :=
"DB2800_ED_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_FUNCTION
@@ -0,0 +1,359 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_3hd";
S7_BlockNumber := "2801";
S7_BlockTitle := "MLC_3mR";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2801_ZC_EA_MUX" : Void
VAR_INPUT
{ S7_MLC := "MLC_3Vi" }
Arranque : Bool;
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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
El bloque implementa la gestión de multiplexado estático para el dispositivo entrada analogica, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_EA`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2001_EA.EA[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `EA[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2001_EA` |
| UDT | `ZC_DISP_EA`, `ZC_DISP_MUX_EA` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2801_EA_MUX".Mux.Scada[#for_mux].Index :=
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2801_EA_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2801_EA_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2801_EA_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_EA" THEN
"DB2801_EA_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_EA";
END_IF;
END_REGION LIMITES
// Traspaso de limites a variable temporal para evitar cambios no deseados
REGION INDICES
#t_Index := "DB2801_EA_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2801_EA_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA := "DB2001_EA".EA[#t_Index];
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA := "DB2001_EA".EA[#t_Index];
END_IF;
// ===========================================================================================================
// Valor anterior del indice
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Hmi_Estado := "DB2001_EA".EA[#t_Index].Hmi_Estado;
IF NOT "DB2001_EA".EA[#t_Index].Estado_AutoMan THEN
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual := "DB2001_EA".EA[#t_Index].Orden_ConsignaManual;
END_IF;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_ValorIngenieria := "DB2001_EA".EA[#t_Index].Estado_ValorIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_ValorTarjeta := "DB2001_EA".EA[#t_Index].Estado_ValorTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_General := "DB2001_EA".EA[#t_Index].Alarmas_General;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_HiloRoto := "DB2001_EA".EA[#t_Index].Alarmas_HiloRoto;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_Lectura := "DB2001_EA".EA[#t_Index].Alarmas_Lectura;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_Max := "DB2001_EA".EA[#t_Index].Alarmas_Max;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_Min := "DB2001_EA".EA[#t_Index].Alarmas_Min;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_Parametros := "DB2001_EA".EA[#t_Index].Alarmas_Parametros;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Aux_Estado := "DB2001_EA".EA[#t_Index].Aux_Estado;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Aux_oldAlarma := "DB2001_EA".EA[#t_Index].Aux_oldAlarma;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Aux_oldAutoMan := "DB2001_EA".EA[#t_Index].Aux_oldAutoMan;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_GrupoAlarma := "DB2001_EA".EA[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2001_EA".EA[#t_Index].Estado_AutoMan <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Estado_AutoMan THEN
// Cambio desde HMI
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan := "DB2001_EA".EA[#t_Index].Estado_AutoMan;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Estado_AutoMan := "DB2001_EA".EA[#t_Index].Estado_AutoMan;
ELSIF "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2001_EA".EA[#t_Index].Estado_AutoMan := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Estado_AutoMan := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2001_EA".EA[#t_Index].Orden_ConsignaManual <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Orden_ConsignaManual THEN
// Cambio desde HMI
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual := "DB2001_EA".EA[#t_Index].Orden_ConsignaManual;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Orden_ConsignaManual := "DB2001_EA".EA[#t_Index].Orden_ConsignaManual;
ELSIF "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Orden_ConsignaManual THEN
// Cambio desde Multiplexado
"DB2001_EA".EA[#t_Index].Orden_ConsignaManual := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Orden_ConsignaManual := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_DireccionEntradaByte <> "DB2001_EA".EA[#t_Index].Config_DireccionEntradaByte)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxHisteresis <> "DB2001_EA".EA[#t_Index].Config_ErrorMaxHisteresis)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxIngenieria <> "DB2001_EA".EA[#t_Index].Config_ErrorMaxIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinHisteresis <> "DB2001_EA".EA[#t_Index].Config_ErrorMinHisteresis)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinIngenieria <> "DB2001_EA".EA[#t_Index].Config_ErrorMinIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxIngenieria <> "DB2001_EA".EA[#t_Index].Config_EscaladoMaxIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxTarjeta <> "DB2001_EA".EA[#t_Index].Config_EscaladoMaxTarjeta)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinIngenieria <> "DB2001_EA".EA[#t_Index].Config_EscaladoMinIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinTarjeta <> "DB2001_EA".EA[#t_Index].Config_EscaladoMinTarjeta)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoOffset <> "DB2001_EA".EA[#t_Index].Config_EscaladoOffset)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_Habilitar <> "DB2001_EA".EA[#t_Index].Config_Habilitar)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMax <> "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMax)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMin <> "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMin)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_TipoAcceso <> "DB2001_EA".EA[#t_Index].Config_TipoAcceso)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HisteresisTipo <> "DB2001_EA".EA[#t_Index].Config_HisteresisTipo);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_DireccionEntradaByte <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_DireccionEntradaByte)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxHisteresis <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxHisteresis)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxIngenieria <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinHisteresis <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinHisteresis)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinIngenieria <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxIngenieria <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxTarjeta <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxTarjeta)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinIngenieria <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinTarjeta <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinTarjeta)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoOffset <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoOffset)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_Habilitar <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_Habilitar)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMax <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMax)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMin <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMin)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_TipoAcceso <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_TipoAcceso)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HisteresisTipo <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HisteresisTipo);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan := "DB2001_EA".EA[#t_Index].Estado_AutoMan;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual := "DB2001_EA".EA[#t_Index].Orden_ConsignaManual;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_DireccionEntradaByte := "DB2001_EA".EA[#t_Index].Config_DireccionEntradaByte;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxHisteresis := "DB2001_EA".EA[#t_Index].Config_ErrorMaxHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxIngenieria := "DB2001_EA".EA[#t_Index].Config_ErrorMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinHisteresis := "DB2001_EA".EA[#t_Index].Config_ErrorMinHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinIngenieria := "DB2001_EA".EA[#t_Index].Config_ErrorMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxIngenieria := "DB2001_EA".EA[#t_Index].Config_EscaladoMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxTarjeta := "DB2001_EA".EA[#t_Index].Config_EscaladoMaxTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinIngenieria := "DB2001_EA".EA[#t_Index].Config_EscaladoMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinTarjeta := "DB2001_EA".EA[#t_Index].Config_EscaladoMinTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoOffset := "DB2001_EA".EA[#t_Index].Config_EscaladoOffset;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_Habilitar := "DB2001_EA".EA[#t_Index].Config_Habilitar;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMax := "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMax;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMin := "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMin;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_TipoAcceso := "DB2001_EA".EA[#t_Index].Config_TipoAcceso;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HisteresisTipo := "DB2001_EA".EA[#t_Index].Config_HisteresisTipo;
// Actualizamos el estado original para configuraciones
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_DireccionEntradaByte := "DB2001_EA".EA[#t_Index].Config_DireccionEntradaByte;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxHisteresis := "DB2001_EA".EA[#t_Index].Config_ErrorMaxHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxIngenieria := "DB2001_EA".EA[#t_Index].Config_ErrorMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinHisteresis := "DB2001_EA".EA[#t_Index].Config_ErrorMinHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinIngenieria := "DB2001_EA".EA[#t_Index].Config_ErrorMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxIngenieria := "DB2001_EA".EA[#t_Index].Config_EscaladoMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxTarjeta := "DB2001_EA".EA[#t_Index].Config_EscaladoMaxTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinIngenieria := "DB2001_EA".EA[#t_Index].Config_EscaladoMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinTarjeta := "DB2001_EA".EA[#t_Index].Config_EscaladoMinTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoOffset := "DB2001_EA".EA[#t_Index].Config_EscaladoOffset;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_Habilitar := "DB2001_EA".EA[#t_Index].Config_Habilitar;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMax := "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMax;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMin := "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMin;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_TipoAcceso := "DB2001_EA".EA[#t_Index].Config_TipoAcceso;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HisteresisTipo := "DB2001_EA".EA[#t_Index].Config_HisteresisTipo;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2001_EA".EA[#t_Index].Estado_AutoMan := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan;
"DB2001_EA".EA[#t_Index].Orden_ConsignaManual := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual;
"DB2001_EA".EA[#t_Index].Config_DireccionEntradaByte := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_DireccionEntradaByte;
"DB2001_EA".EA[#t_Index].Config_ErrorMaxHisteresis := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxHisteresis;
"DB2001_EA".EA[#t_Index].Config_ErrorMaxIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxIngenieria;
"DB2001_EA".EA[#t_Index].Config_ErrorMinHisteresis := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinHisteresis;
"DB2001_EA".EA[#t_Index].Config_ErrorMinIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinIngenieria;
"DB2001_EA".EA[#t_Index].Config_EscaladoMaxIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxIngenieria;
"DB2001_EA".EA[#t_Index].Config_EscaladoMaxTarjeta := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxTarjeta;
"DB2001_EA".EA[#t_Index].Config_EscaladoMinIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinIngenieria;
"DB2001_EA".EA[#t_Index].Config_EscaladoMinTarjeta := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinTarjeta;
"DB2001_EA".EA[#t_Index].Config_EscaladoOffset := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoOffset;
"DB2001_EA".EA[#t_Index].Config_Habilitar := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_Habilitar;
"DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMax := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMax;
"DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMin := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMin;
"DB2001_EA".EA[#t_Index].Config_TipoAcceso := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_TipoAcceso;
"DB2001_EA".EA[#t_Index].Config_HisteresisTipo := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HisteresisTipo;
// Actualizamos el estado original para configuraciones
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_DireccionEntradaByte := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_DireccionEntradaByte;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxHisteresis := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinHisteresis := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxTarjeta := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinTarjeta := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoOffset := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoOffset;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_Habilitar := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_Habilitar;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMax := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMax;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMin := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMin;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_TipoAcceso := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_TipoAcceso;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HisteresisTipo := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HisteresisTipo;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la EA[0] que no se usa para escribir el index.
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA := "DB2001_EA".EA[0];
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA := "DB2001_EA".EA[0];
// Actualizacion de estado anterior
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2801_EA_MUX".Mux.Scada[#for_mux].Index :=
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_3mR
es-ES: FC2801_ZC_EA_MUX
- id: MLC_3hd
es-ES: Funcion de gestion multiplexado dispositivo Entrada analogica
- id: MLC_3Vi
es-ES: Primer arranque
@@ -1,355 +0,0 @@
FUNCTION "FC2801_ZC_EA_MUX" : Void
TITLE = FC2801_ZC_EA_MUX
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion multiplexado dispositivo Entrada analogica
VAR_INPUT
Arranque : Bool; // Primer arranque
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
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
El bloque implementa la gestión de multiplexado estático para el dispositivo entrada analogica, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_EA`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2001_EA.EA[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `EA[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2001_EA` |
| UDT | `ZC_DISP_EA`, `ZC_DISP_MUX_EA` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2801_EA_MUX".Mux.Scada[#for_mux].Index :=
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2801_EA_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2801_EA_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2801_EA_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_EA" THEN
"DB2801_EA_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_EA";
END_IF;
END_REGION LIMITES
// Traspaso de limites a variable temporal para evitar cambios no deseados
REGION INDICES
#t_Index := "DB2801_EA_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2801_EA_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA := "DB2001_EA".EA[#t_Index];
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA := "DB2001_EA".EA[#t_Index];
END_IF;
// ===========================================================================================================
// Valor anterior del indice
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Hmi_Estado := "DB2001_EA".EA[#t_Index].Hmi_Estado;
IF NOT "DB2001_EA".EA[#t_Index].Estado_AutoMan THEN
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual := "DB2001_EA".EA[#t_Index].Orden_ConsignaManual;
END_IF;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_ValorIngenieria := "DB2001_EA".EA[#t_Index].Estado_ValorIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_ValorTarjeta := "DB2001_EA".EA[#t_Index].Estado_ValorTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_General := "DB2001_EA".EA[#t_Index].Alarmas_General;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_HiloRoto := "DB2001_EA".EA[#t_Index].Alarmas_HiloRoto;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_Lectura := "DB2001_EA".EA[#t_Index].Alarmas_Lectura;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_Max := "DB2001_EA".EA[#t_Index].Alarmas_Max;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_Min := "DB2001_EA".EA[#t_Index].Alarmas_Min;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Alarmas_Parametros := "DB2001_EA".EA[#t_Index].Alarmas_Parametros;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Aux_Estado := "DB2001_EA".EA[#t_Index].Aux_Estado;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Aux_oldAlarma := "DB2001_EA".EA[#t_Index].Aux_oldAlarma;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Aux_oldAutoMan := "DB2001_EA".EA[#t_Index].Aux_oldAutoMan;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_GrupoAlarma := "DB2001_EA".EA[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2001_EA".EA[#t_Index].Estado_AutoMan <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Estado_AutoMan THEN
// Cambio desde HMI
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan := "DB2001_EA".EA[#t_Index].Estado_AutoMan;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Estado_AutoMan := "DB2001_EA".EA[#t_Index].Estado_AutoMan;
ELSIF "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2001_EA".EA[#t_Index].Estado_AutoMan := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Estado_AutoMan := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2001_EA".EA[#t_Index].Orden_ConsignaManual <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Orden_ConsignaManual THEN
// Cambio desde HMI
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual := "DB2001_EA".EA[#t_Index].Orden_ConsignaManual;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Orden_ConsignaManual := "DB2001_EA".EA[#t_Index].Orden_ConsignaManual;
ELSIF "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Orden_ConsignaManual THEN
// Cambio desde Multiplexado
"DB2001_EA".EA[#t_Index].Orden_ConsignaManual := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Orden_ConsignaManual := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_DireccionEntradaByte <> "DB2001_EA".EA[#t_Index].Config_DireccionEntradaByte)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxHisteresis <> "DB2001_EA".EA[#t_Index].Config_ErrorMaxHisteresis)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxIngenieria <> "DB2001_EA".EA[#t_Index].Config_ErrorMaxIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinHisteresis <> "DB2001_EA".EA[#t_Index].Config_ErrorMinHisteresis)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinIngenieria <> "DB2001_EA".EA[#t_Index].Config_ErrorMinIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxIngenieria <> "DB2001_EA".EA[#t_Index].Config_EscaladoMaxIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxTarjeta <> "DB2001_EA".EA[#t_Index].Config_EscaladoMaxTarjeta)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinIngenieria <> "DB2001_EA".EA[#t_Index].Config_EscaladoMinIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinTarjeta <> "DB2001_EA".EA[#t_Index].Config_EscaladoMinTarjeta)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoOffset <> "DB2001_EA".EA[#t_Index].Config_EscaladoOffset)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_Habilitar <> "DB2001_EA".EA[#t_Index].Config_Habilitar)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMax <> "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMax)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMin <> "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMin)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_TipoAcceso <> "DB2001_EA".EA[#t_Index].Config_TipoAcceso)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HisteresisTipo <> "DB2001_EA".EA[#t_Index].Config_HisteresisTipo);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_DireccionEntradaByte <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_DireccionEntradaByte)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxHisteresis <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxHisteresis)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxIngenieria <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinHisteresis <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinHisteresis)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinIngenieria <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxIngenieria <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxTarjeta <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxTarjeta)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinIngenieria <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinIngenieria)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinTarjeta <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinTarjeta)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoOffset <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoOffset)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_Habilitar <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_Habilitar)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMax <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMax)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMin <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMin)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_TipoAcceso <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_TipoAcceso)
OR ("DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HisteresisTipo <> "DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HisteresisTipo);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan := "DB2001_EA".EA[#t_Index].Estado_AutoMan;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual := "DB2001_EA".EA[#t_Index].Orden_ConsignaManual;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_DireccionEntradaByte := "DB2001_EA".EA[#t_Index].Config_DireccionEntradaByte;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxHisteresis := "DB2001_EA".EA[#t_Index].Config_ErrorMaxHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxIngenieria := "DB2001_EA".EA[#t_Index].Config_ErrorMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinHisteresis := "DB2001_EA".EA[#t_Index].Config_ErrorMinHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinIngenieria := "DB2001_EA".EA[#t_Index].Config_ErrorMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxIngenieria := "DB2001_EA".EA[#t_Index].Config_EscaladoMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxTarjeta := "DB2001_EA".EA[#t_Index].Config_EscaladoMaxTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinIngenieria := "DB2001_EA".EA[#t_Index].Config_EscaladoMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinTarjeta := "DB2001_EA".EA[#t_Index].Config_EscaladoMinTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoOffset := "DB2001_EA".EA[#t_Index].Config_EscaladoOffset;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_Habilitar := "DB2001_EA".EA[#t_Index].Config_Habilitar;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMax := "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMax;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMin := "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMin;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_TipoAcceso := "DB2001_EA".EA[#t_Index].Config_TipoAcceso;
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HisteresisTipo := "DB2001_EA".EA[#t_Index].Config_HisteresisTipo;
// Actualizamos el estado original para configuraciones
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_DireccionEntradaByte := "DB2001_EA".EA[#t_Index].Config_DireccionEntradaByte;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxHisteresis := "DB2001_EA".EA[#t_Index].Config_ErrorMaxHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxIngenieria := "DB2001_EA".EA[#t_Index].Config_ErrorMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinHisteresis := "DB2001_EA".EA[#t_Index].Config_ErrorMinHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinIngenieria := "DB2001_EA".EA[#t_Index].Config_ErrorMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxIngenieria := "DB2001_EA".EA[#t_Index].Config_EscaladoMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxTarjeta := "DB2001_EA".EA[#t_Index].Config_EscaladoMaxTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinIngenieria := "DB2001_EA".EA[#t_Index].Config_EscaladoMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinTarjeta := "DB2001_EA".EA[#t_Index].Config_EscaladoMinTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoOffset := "DB2001_EA".EA[#t_Index].Config_EscaladoOffset;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_Habilitar := "DB2001_EA".EA[#t_Index].Config_Habilitar;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMax := "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMax;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMin := "DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMin;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_TipoAcceso := "DB2001_EA".EA[#t_Index].Config_TipoAcceso;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HisteresisTipo := "DB2001_EA".EA[#t_Index].Config_HisteresisTipo;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2001_EA".EA[#t_Index].Estado_AutoMan := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Estado_AutoMan;
"DB2001_EA".EA[#t_Index].Orden_ConsignaManual := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Orden_ConsignaManual;
"DB2001_EA".EA[#t_Index].Config_DireccionEntradaByte := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_DireccionEntradaByte;
"DB2001_EA".EA[#t_Index].Config_ErrorMaxHisteresis := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxHisteresis;
"DB2001_EA".EA[#t_Index].Config_ErrorMaxIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxIngenieria;
"DB2001_EA".EA[#t_Index].Config_ErrorMinHisteresis := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinHisteresis;
"DB2001_EA".EA[#t_Index].Config_ErrorMinIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinIngenieria;
"DB2001_EA".EA[#t_Index].Config_EscaladoMaxIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxIngenieria;
"DB2001_EA".EA[#t_Index].Config_EscaladoMaxTarjeta := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxTarjeta;
"DB2001_EA".EA[#t_Index].Config_EscaladoMinIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinIngenieria;
"DB2001_EA".EA[#t_Index].Config_EscaladoMinTarjeta := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinTarjeta;
"DB2001_EA".EA[#t_Index].Config_EscaladoOffset := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoOffset;
"DB2001_EA".EA[#t_Index].Config_Habilitar := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_Habilitar;
"DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMax := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMax;
"DB2001_EA".EA[#t_Index].Config_HabilitarAlarmaMin := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMin;
"DB2001_EA".EA[#t_Index].Config_TipoAcceso := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_TipoAcceso;
"DB2001_EA".EA[#t_Index].Config_HisteresisTipo := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HisteresisTipo;
// Actualizamos el estado original para configuraciones
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_DireccionEntradaByte := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_DireccionEntradaByte;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxHisteresis := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMaxIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinHisteresis := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinHisteresis;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_ErrorMinIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_ErrorMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMaxTarjeta := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMaxTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinIngenieria := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinIngenieria;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoMinTarjeta := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoMinTarjeta;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_EscaladoOffset := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_EscaladoOffset;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_Habilitar := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_Habilitar;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMax := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMax;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HabilitarAlarmaMin := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HabilitarAlarmaMin;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_TipoAcceso := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_TipoAcceso;
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA.Config_HisteresisTipo := "DB2801_EA_MUX".Mux.Scada[#for_mux].EA.Config_HisteresisTipo;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la EA[0] que no se usa para escribir el index.
"DB2801_EA_MUX".Mux.Scada[#for_mux].EA := "DB2001_EA".EA[0];
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldEA := "DB2001_EA".EA[0];
// Actualizacion de estado anterior
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2801_EA_MUX".Mux.Scada[#for_mux].Index :=
"DB2801_EA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_FUNCTION
@@ -0,0 +1,321 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4xP";
S7_BlockNumber := "2805";
S7_BlockTitle := "MLC_3Pm";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2805_ZC_SD_MUX" : Void
VAR_INPUT
{ S7_MLC := "MLC_3Tt" }
Arranque : Bool;
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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
El bloque implementa la gestión de multiplexado estático para el dispositivo salida digital, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_SD`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2005_SD.SD[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `SD[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2005_SD` |
| UDT | `ZC_DISP_SD`, `ZC_DISP_MUX_SD` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2805_SD_MUX".Mux.Scada[#for_mux].Index :=
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2805_SD_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2805_SD_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2805_SD_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_SD" THEN
"DB2805_SD_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_SD";
END_IF;
END_REGION LIMITES
// Traspaso de limites a variable temporal para evitar cambios no deseados
REGION INDICES
#t_Index := "DB2805_SD_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2805_SD_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD := "DB2005_SD".SD[#t_Index];
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD := "DB2005_SD".SD[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Hmi_Estado := "DB2005_SD".SD[#t_Index].Hmi_Estado;
IF NOT "DB2005_SD".SD[#t_Index].Estado_AutoMan THEN
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual := "DB2005_SD".SD["DB2805_SD_MUX".Mux.Scada[#for_mux].Index].Orden_ActivarManual;
END_IF;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_Activado := "DB2005_SD".SD[#t_Index].Estado_Activado;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_Enclavado := "DB2005_SD".SD[#t_Index].Estado_Enclavado;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_Salida := "DB2005_SD".SD[#t_Index].Estado_Salida;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_SeguridadOk := "DB2005_SD".SD[#t_Index].Estado_SeguridadOk;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarAuto := "DB2005_SD".SD[#t_Index].Orden_ActivarAuto;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_TiempoAct := "DB2005_SD".SD[#t_Index].Tiempos_TiempoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_TiempoDes := "DB2005_SD".SD[#t_Index].Tiempos_TiempoDes;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Aux_oldActivado := "DB2005_SD".SD[#t_Index].Aux_oldActivado;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Aux_oldAutoMan := "DB2005_SD".SD[#t_Index].Aux_oldAutoMan;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_GrupoAlarma := "DB2005_SD".SD[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2005_SD".SD[#t_Index].Estado_AutoMan <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Estado_AutoMan THEN
// Cambio desde HMI
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan := "DB2005_SD".SD[#t_Index].Estado_AutoMan;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Estado_AutoMan := "DB2005_SD".SD[#t_Index].Estado_AutoMan;
ELSIF "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2005_SD".SD[#t_Index].Estado_AutoMan := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Estado_AutoMan := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2005_SD".SD[#t_Index].Orden_ActivarManual <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual := "DB2005_SD".SD[#t_Index].Orden_ActivarManual;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Orden_ActivarManual := "DB2005_SD".SD[#t_Index].Orden_ActivarManual;
ELSIF "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2005_SD".SD[#t_Index].Orden_ActivarManual := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Orden_ActivarManual := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaBit <> "DB2005_SD".SD[#t_Index].Config_DireccionSalidaBit)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaByte <> "DB2005_SD".SD[#t_Index].Config_DireccionSalidaByte)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_Habilitar <> "DB2005_SD".SD[#t_Index].Config_Habilitar)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarInvertir <> "DB2005_SD".SD[#t_Index].Config_HabilitarInvertir)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoAct <> "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoAct)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoDes <> "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoDes)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_TipoAcceso <> "DB2005_SD".SD[#t_Index].Config_TipoAcceso)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoAct <> "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoAct)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoDes <> "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoDes);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaBit <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaBit)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaByte <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaByte)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_Habilitar <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_Habilitar)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarInvertir <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarInvertir)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoAct <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoAct)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoDes <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoDes)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_TipoAcceso <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_TipoAcceso)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoAct <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoAct)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoDes <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoDes);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan := "DB2005_SD".SD[#t_Index].Estado_AutoMan;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual := "DB2005_SD".SD[#t_Index].Orden_ActivarManual;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaBit := "DB2005_SD".SD[#t_Index].Config_DireccionSalidaBit;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaByte := "DB2005_SD".SD[#t_Index].Config_DireccionSalidaByte;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_Habilitar := "DB2005_SD".SD[#t_Index].Config_Habilitar;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarInvertir := "DB2005_SD".SD[#t_Index].Config_HabilitarInvertir;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoAct := "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoDes := "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoDes;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_TipoAcceso := "DB2005_SD".SD[#t_Index].Config_TipoAcceso;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoAct := "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoDes := "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoDes;
// Actualizamos el estado original para configuraciones
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaBit := "DB2005_SD".SD[#t_Index].Config_DireccionSalidaBit;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaByte := "DB2005_SD".SD[#t_Index].Config_DireccionSalidaByte;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_Habilitar := "DB2005_SD".SD[#t_Index].Config_Habilitar;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarInvertir := "DB2005_SD".SD[#t_Index].Config_HabilitarInvertir;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoAct := "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoDes := "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoDes;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_TipoAcceso := "DB2005_SD".SD[#t_Index].Config_TipoAcceso;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoAct := "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoDes := "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoDes;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2005_SD".SD[#t_Index].Estado_AutoMan := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan;
"DB2005_SD".SD[#t_Index].Orden_ActivarManual := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual;
"DB2005_SD".SD[#t_Index].Config_DireccionSalidaBit := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaBit;
"DB2005_SD".SD[#t_Index].Config_DireccionSalidaByte := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaByte;
"DB2005_SD".SD[#t_Index].Config_Habilitar := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_Habilitar;
"DB2005_SD".SD[#t_Index].Config_HabilitarInvertir := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarInvertir;
"DB2005_SD".SD[#t_Index].Config_HabilitarRetardoAct := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoAct;
"DB2005_SD".SD[#t_Index].Config_HabilitarRetardoDes := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoDes;
"DB2005_SD".SD[#t_Index].Config_TipoAcceso := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_TipoAcceso;
"DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoAct := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoAct;
"DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoDes := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoDes;
// Actualizamos el estado original para configuraciones
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaBit := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaBit;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaByte := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaByte;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_Habilitar := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_Habilitar;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarInvertir := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarInvertir;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoAct := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoDes := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoDes;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_TipoAcceso := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_TipoAcceso;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoAct := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoDes := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoDes;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la SD[0] que no se usa para escribir el index.
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD := "DB2005_SD".SD[0];
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD := "DB2005_SD".SD[0];
// Actualizacion de estado anterior
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
"DB2805_SD_MUX".Mux.Scada[#for_mux].Index :=
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_3Pm
es-ES: FC2805_ZC_SD_MUX
- id: MLC_4xP
es-ES: Funcion de gestion multiplexado dispositivo Salida digital
- id: MLC_3Tt
es-ES: Primer arranque
@@ -1,317 +0,0 @@
FUNCTION "FC2805_ZC_SD_MUX" : Void
TITLE = FC2805_ZC_SD_MUX
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion multiplexado dispositivo Salida digital
VAR_INPUT
Arranque : Bool; // Primer arranque
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
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
El bloque implementa la gestión de multiplexado estático para el dispositivo salida digital, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_SD`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2005_SD.SD[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `SD[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2005_SD` |
| UDT | `ZC_DISP_SD`, `ZC_DISP_MUX_SD` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2805_SD_MUX".Mux.Scada[#for_mux].Index :=
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2805_SD_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2805_SD_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2805_SD_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_SD" THEN
"DB2805_SD_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_SD";
END_IF;
END_REGION LIMITES
// Traspaso de limites a variable temporal para evitar cambios no deseados
REGION INDICES
#t_Index := "DB2805_SD_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2805_SD_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD := "DB2005_SD".SD[#t_Index];
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD := "DB2005_SD".SD[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Hmi_Estado := "DB2005_SD".SD[#t_Index].Hmi_Estado;
IF NOT "DB2005_SD".SD[#t_Index].Estado_AutoMan THEN
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual := "DB2005_SD".SD["DB2805_SD_MUX".Mux.Scada[#for_mux].Index].Orden_ActivarManual;
END_IF;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_Activado := "DB2005_SD".SD[#t_Index].Estado_Activado;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_Enclavado := "DB2005_SD".SD[#t_Index].Estado_Enclavado;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_Salida := "DB2005_SD".SD[#t_Index].Estado_Salida;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_SeguridadOk := "DB2005_SD".SD[#t_Index].Estado_SeguridadOk;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarAuto := "DB2005_SD".SD[#t_Index].Orden_ActivarAuto;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_TiempoAct := "DB2005_SD".SD[#t_Index].Tiempos_TiempoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_TiempoDes := "DB2005_SD".SD[#t_Index].Tiempos_TiempoDes;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Aux_oldActivado := "DB2005_SD".SD[#t_Index].Aux_oldActivado;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Aux_oldAutoMan := "DB2005_SD".SD[#t_Index].Aux_oldAutoMan;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_GrupoAlarma := "DB2005_SD".SD[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2005_SD".SD[#t_Index].Estado_AutoMan <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Estado_AutoMan THEN
// Cambio desde HMI
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan := "DB2005_SD".SD[#t_Index].Estado_AutoMan;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Estado_AutoMan := "DB2005_SD".SD[#t_Index].Estado_AutoMan;
ELSIF "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2005_SD".SD[#t_Index].Estado_AutoMan := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Estado_AutoMan := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2005_SD".SD[#t_Index].Orden_ActivarManual <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual := "DB2005_SD".SD[#t_Index].Orden_ActivarManual;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Orden_ActivarManual := "DB2005_SD".SD[#t_Index].Orden_ActivarManual;
ELSIF "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2005_SD".SD[#t_Index].Orden_ActivarManual := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Orden_ActivarManual := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaBit <> "DB2005_SD".SD[#t_Index].Config_DireccionSalidaBit)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaByte <> "DB2005_SD".SD[#t_Index].Config_DireccionSalidaByte)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_Habilitar <> "DB2005_SD".SD[#t_Index].Config_Habilitar)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarInvertir <> "DB2005_SD".SD[#t_Index].Config_HabilitarInvertir)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoAct <> "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoAct)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoDes <> "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoDes)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_TipoAcceso <> "DB2005_SD".SD[#t_Index].Config_TipoAcceso)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoAct <> "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoAct)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoDes <> "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoDes);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaBit <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaBit)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaByte <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaByte)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_Habilitar <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_Habilitar)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarInvertir <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarInvertir)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoAct <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoAct)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoDes <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoDes)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_TipoAcceso <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_TipoAcceso)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoAct <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoAct)
OR ("DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoDes <> "DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoDes);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan := "DB2005_SD".SD[#t_Index].Estado_AutoMan;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual := "DB2005_SD".SD[#t_Index].Orden_ActivarManual;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaBit := "DB2005_SD".SD[#t_Index].Config_DireccionSalidaBit;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaByte := "DB2005_SD".SD[#t_Index].Config_DireccionSalidaByte;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_Habilitar := "DB2005_SD".SD[#t_Index].Config_Habilitar;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarInvertir := "DB2005_SD".SD[#t_Index].Config_HabilitarInvertir;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoAct := "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoDes := "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoDes;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_TipoAcceso := "DB2005_SD".SD[#t_Index].Config_TipoAcceso;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoAct := "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoDes := "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoDes;
// Actualizamos el estado original para configuraciones
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaBit := "DB2005_SD".SD[#t_Index].Config_DireccionSalidaBit;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaByte := "DB2005_SD".SD[#t_Index].Config_DireccionSalidaByte;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_Habilitar := "DB2005_SD".SD[#t_Index].Config_Habilitar;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarInvertir := "DB2005_SD".SD[#t_Index].Config_HabilitarInvertir;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoAct := "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoDes := "DB2005_SD".SD[#t_Index].Config_HabilitarRetardoDes;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_TipoAcceso := "DB2005_SD".SD[#t_Index].Config_TipoAcceso;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoAct := "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoDes := "DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoDes;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2005_SD".SD[#t_Index].Estado_AutoMan := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Estado_AutoMan;
"DB2005_SD".SD[#t_Index].Orden_ActivarManual := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Orden_ActivarManual;
"DB2005_SD".SD[#t_Index].Config_DireccionSalidaBit := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaBit;
"DB2005_SD".SD[#t_Index].Config_DireccionSalidaByte := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaByte;
"DB2005_SD".SD[#t_Index].Config_Habilitar := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_Habilitar;
"DB2005_SD".SD[#t_Index].Config_HabilitarInvertir := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarInvertir;
"DB2005_SD".SD[#t_Index].Config_HabilitarRetardoAct := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoAct;
"DB2005_SD".SD[#t_Index].Config_HabilitarRetardoDes := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoDes;
"DB2005_SD".SD[#t_Index].Config_TipoAcceso := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_TipoAcceso;
"DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoAct := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoAct;
"DB2005_SD".SD[#t_Index].Tiempos_SetPointTiempoDes := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoDes;
// Actualizamos el estado original para configuraciones
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaBit := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaBit;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_DireccionSalidaByte := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_DireccionSalidaByte;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_Habilitar := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_Habilitar;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarInvertir := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarInvertir;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoAct := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_HabilitarRetardoDes := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_HabilitarRetardoDes;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Config_TipoAcceso := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Config_TipoAcceso;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoAct := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoAct;
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD.Tiempos_SetPointTiempoDes := "DB2805_SD_MUX".Mux.Scada[#for_mux].SD.Tiempos_SetPointTiempoDes;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la SD[0] que no se usa para escribir el index.
"DB2805_SD_MUX".Mux.Scada[#for_mux].SD := "DB2005_SD".SD[0];
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldSD := "DB2005_SD".SD[0];
// Actualizacion de estado anterior
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
"DB2805_SD_MUX".Mux.Scada[#for_mux].Index :=
"DB2805_SD_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_FUNCTION
@@ -0,0 +1,358 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_3wH";
S7_BlockNumber := "2806";
S7_BlockTitle := "MLC_uY";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2806_ZC_SA_MUX" : Void
VAR_INPUT
{ S7_MLC := "MLC_5C3" }
Arranque : Bool;
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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
El bloque implementa la gestión de multiplexado estático para el dispositivo salida analogica, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_SA`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2006_SA.SA[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `SA[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2006_SA` |
| UDT | `ZC_DISP_SA`, `ZC_DISP_MUX_SA` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2806_SA_MUX".Mux.Scada[#for_mux].Index :=
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2806_SA_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2806_SA_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2806_SA_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_SA" THEN
"DB2806_SA_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_SA";
END_IF;
END_REGION LIMITES
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2806_SA_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2806_SA_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA := "DB2006_SA".SA[#t_Index];
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA := "DB2006_SA".SA[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Hmi_Estado := "DB2006_SA".SA[#t_Index].Hmi_Estado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_SeguridadOk := "DB2006_SA".SA[#t_Index].Estado_SeguridadOk;
IF NOT "DB2006_SA".SA[#t_Index].Estado_AutoMan THEN
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual := "DB2006_SA".SA[#t_Index].Orden_ConsignaManual;
END_IF;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_ValorActual := "DB2006_SA".SA[#t_Index].Estado_ValorActual;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_ValorTarjeta := "DB2006_SA".SA[#t_Index].Estado_ValorTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_Enclavado := "DB2006_SA".SA[#t_Index].Estado_Enclavado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_Escritura := "DB2006_SA".SA[#t_Index].Alarmas_Escritura;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_General := "DB2006_SA".SA[#t_Index].Alarmas_General;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_Max := "DB2006_SA".SA[#t_Index].Alarmas_Max;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_Min := "DB2006_SA".SA[#t_Index].Alarmas_Min;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_Parametros := "DB2006_SA".SA[#t_Index].Alarmas_Parametros;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaAuto := "DB2006_SA".SA[#t_Index].Orden_ConsignaAuto;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Aux_Estado := "DB2006_SA".SA[#t_Index].Aux_Estado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Aux_oldAlarma := "DB2006_SA".SA[#t_Index].Aux_oldAlarma;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Aux_oldAutoMan := "DB2006_SA".SA[#t_Index].Aux_oldAutoMan;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_GrupoAlarma := "DB2006_SA".SA[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2006_SA".SA[#t_Index].Estado_AutoMan <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Estado_AutoMan THEN
// Cambio desde HMI
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan := "DB2006_SA".SA[#t_Index].Estado_AutoMan;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Estado_AutoMan := "DB2006_SA".SA[#t_Index].Estado_AutoMan;
ELSIF "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2006_SA".SA[#t_Index].Estado_AutoMan := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Estado_AutoMan := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Consigna Manual
IF "DB2006_SA".SA[#t_Index].Orden_ConsignaManual <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaManual THEN
// Cambio desde HMI
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual := "DB2006_SA".SA[#t_Index].Orden_ConsignaManual;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaManual := "DB2006_SA".SA[#t_Index].Orden_ConsignaManual;
ELSIF "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaManual THEN
// Cambio desde Multiplexado
"DB2006_SA".SA[#t_Index].Orden_ConsignaManual := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaManual := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual;
END_IF;
// ===========================================================================================================
// Orden Consigna enclavado
IF "DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaEnclavado THEN
// Cambio desde HMI
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado := "DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaEnclavado := "DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado;
ELSIF "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaEnclavado THEN
// Cambio desde Multiplexado
"DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaEnclavado := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_DireccionSalidaByte <> "DB2006_SA".SA[#t_Index].Config_DireccionSalidaByte)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMaxIngenieria <> "DB2006_SA".SA[#t_Index].Config_ErrorMaxIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMinIngenieria <> "DB2006_SA".SA[#t_Index].Config_ErrorMinIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxIngenieria <> "DB2006_SA".SA[#t_Index].Config_EscaladoMaxIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxTarjeta <> "DB2006_SA".SA[#t_Index].Config_EscaladoMaxTarjeta)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinIngenieria <> "DB2006_SA".SA[#t_Index].Config_EscaladoMinIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinTarjeta <> "DB2006_SA".SA[#t_Index].Config_EscaladoMinTarjeta)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoOffset <> "DB2006_SA".SA[#t_Index].Config_EscaladoOffset)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_Habilitar <> "DB2006_SA".SA[#t_Index].Config_Habilitar)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMax <> "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMax)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMin <> "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMin)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_TipoAcceso <> "DB2006_SA".SA[#t_Index].Config_TipoAcceso);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_DireccionSalidaByte <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_DireccionSalidaByte)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMaxIngenieria <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMaxIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMinIngenieria <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMinIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxIngenieria <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxTarjeta <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxTarjeta)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinIngenieria <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinTarjeta <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinTarjeta)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoOffset <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoOffset)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_Habilitar <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_Habilitar)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMax <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMax)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMin <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMin)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_TipoAcceso <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_TipoAcceso);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan := "DB2006_SA".SA[#t_Index].Estado_AutoMan;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual := "DB2006_SA".SA[#t_Index].Orden_ConsignaManual;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado := "DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_DireccionSalidaByte := "DB2006_SA".SA[#t_Index].Config_DireccionSalidaByte;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMaxIngenieria := "DB2006_SA".SA[#t_Index].Config_ErrorMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMinIngenieria := "DB2006_SA".SA[#t_Index].Config_ErrorMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxIngenieria := "DB2006_SA".SA[#t_Index].Config_EscaladoMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxTarjeta := "DB2006_SA".SA[#t_Index].Config_EscaladoMaxTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinIngenieria := "DB2006_SA".SA[#t_Index].Config_EscaladoMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinTarjeta := "DB2006_SA".SA[#t_Index].Config_EscaladoMinTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoOffset := "DB2006_SA".SA[#t_Index].Config_EscaladoOffset;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_Habilitar := "DB2006_SA".SA[#t_Index].Config_Habilitar;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMax := "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMax;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMin := "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMin;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_TipoAcceso := "DB2006_SA".SA[#t_Index].Config_TipoAcceso;
// Actualizamos el estado original para configuraciones
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_DireccionSalidaByte := "DB2006_SA".SA[#t_Index].Config_DireccionSalidaByte;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMaxIngenieria := "DB2006_SA".SA[#t_Index].Config_ErrorMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMinIngenieria := "DB2006_SA".SA[#t_Index].Config_ErrorMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxIngenieria := "DB2006_SA".SA[#t_Index].Config_EscaladoMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxTarjeta := "DB2006_SA".SA[#t_Index].Config_EscaladoMaxTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinIngenieria := "DB2006_SA".SA[#t_Index].Config_EscaladoMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinTarjeta := "DB2006_SA".SA[#t_Index].Config_EscaladoMinTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoOffset := "DB2006_SA".SA[#t_Index].Config_EscaladoOffset;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_Habilitar := "DB2006_SA".SA[#t_Index].Config_Habilitar;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMax := "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMax;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMin := "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMin;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_TipoAcceso := "DB2006_SA".SA[#t_Index].Config_TipoAcceso;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2006_SA".SA[#t_Index].Estado_AutoMan := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan;
"DB2006_SA".SA[#t_Index].Orden_ConsignaManual := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual;
"DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado;
"DB2006_SA".SA[#t_Index].Config_DireccionSalidaByte := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_DireccionSalidaByte;
"DB2006_SA".SA[#t_Index].Config_ErrorMaxIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMaxIngenieria;
"DB2006_SA".SA[#t_Index].Config_ErrorMinIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMinIngenieria;
"DB2006_SA".SA[#t_Index].Config_EscaladoMaxIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxIngenieria;
"DB2006_SA".SA[#t_Index].Config_EscaladoMaxTarjeta := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxTarjeta;
"DB2006_SA".SA[#t_Index].Config_EscaladoMinIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinIngenieria;
"DB2006_SA".SA[#t_Index].Config_EscaladoMinTarjeta := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinTarjeta;
"DB2006_SA".SA[#t_Index].Config_EscaladoOffset := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoOffset;
"DB2006_SA".SA[#t_Index].Config_Habilitar := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_Habilitar;
"DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMax := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMax;
"DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMin := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMin;
"DB2006_SA".SA[#t_Index].Config_TipoAcceso := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_TipoAcceso;
// Actualizamos el estado original para configuraciones
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_DireccionSalidaByte := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_DireccionSalidaByte;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMaxIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMinIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxTarjeta := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinTarjeta := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoOffset := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoOffset;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_Habilitar := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_Habilitar;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMax := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMax;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMin := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMin;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_TipoAcceso := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_TipoAcceso;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la SA[0] que no se usa para escribir el index.
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA := "DB2006_SA".SA[0];
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA := "DB2006_SA".SA[0];
// Actualizacion de estado anterior
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2806_SA_MUX".Mux.Scada[#for_mux].Index :=
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,7 @@
MultiLingualTexts:
- id: MLC_uY
es-ES: FC2806_ZC_SA_MUX
- id: MLC_3wH
es-ES: Funcion de gestion multiplexado dispositivo Salida analogica
- id: MLC_5C3
es-ES: Primer arranque
@@ -1,354 +0,0 @@
FUNCTION "FC2806_ZC_SA_MUX" : Void
TITLE = FC2806_ZC_SA_MUX
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion multiplexado dispositivo Salida analogica
VAR_INPUT
Arranque : Bool; // Primer arranque
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
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
El bloque implementa la gestión de multiplexado estático para el dispositivo salida analogica, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_SA`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2006_SA.SA[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `SA[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2006_SA` |
| UDT | `ZC_DISP_SA`, `ZC_DISP_MUX_SA` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2806_SA_MUX".Mux.Scada[#for_mux].Index :=
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2806_SA_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2806_SA_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2806_SA_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_SA" THEN
"DB2806_SA_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_SA";
END_IF;
END_REGION LIMITES
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2806_SA_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2806_SA_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA := "DB2006_SA".SA[#t_Index];
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA := "DB2006_SA".SA[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Hmi_Estado := "DB2006_SA".SA[#t_Index].Hmi_Estado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_SeguridadOk := "DB2006_SA".SA[#t_Index].Estado_SeguridadOk;
IF NOT "DB2006_SA".SA[#t_Index].Estado_AutoMan THEN
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual := "DB2006_SA".SA[#t_Index].Orden_ConsignaManual;
END_IF;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_ValorActual := "DB2006_SA".SA[#t_Index].Estado_ValorActual;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_ValorTarjeta := "DB2006_SA".SA[#t_Index].Estado_ValorTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_Enclavado := "DB2006_SA".SA[#t_Index].Estado_Enclavado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_Escritura := "DB2006_SA".SA[#t_Index].Alarmas_Escritura;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_General := "DB2006_SA".SA[#t_Index].Alarmas_General;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_Max := "DB2006_SA".SA[#t_Index].Alarmas_Max;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_Min := "DB2006_SA".SA[#t_Index].Alarmas_Min;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Alarmas_Parametros := "DB2006_SA".SA[#t_Index].Alarmas_Parametros;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaAuto := "DB2006_SA".SA[#t_Index].Orden_ConsignaAuto;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Aux_Estado := "DB2006_SA".SA[#t_Index].Aux_Estado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Aux_oldAlarma := "DB2006_SA".SA[#t_Index].Aux_oldAlarma;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Aux_oldAutoMan := "DB2006_SA".SA[#t_Index].Aux_oldAutoMan;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_GrupoAlarma := "DB2006_SA".SA[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2006_SA".SA[#t_Index].Estado_AutoMan <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Estado_AutoMan THEN
// Cambio desde HMI
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan := "DB2006_SA".SA[#t_Index].Estado_AutoMan;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Estado_AutoMan := "DB2006_SA".SA[#t_Index].Estado_AutoMan;
ELSIF "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2006_SA".SA[#t_Index].Estado_AutoMan := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Estado_AutoMan := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Consigna Manual
IF "DB2006_SA".SA[#t_Index].Orden_ConsignaManual <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaManual THEN
// Cambio desde HMI
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual := "DB2006_SA".SA[#t_Index].Orden_ConsignaManual;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaManual := "DB2006_SA".SA[#t_Index].Orden_ConsignaManual;
ELSIF "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaManual THEN
// Cambio desde Multiplexado
"DB2006_SA".SA[#t_Index].Orden_ConsignaManual := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaManual := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual;
END_IF;
// ===========================================================================================================
// Orden Consigna enclavado
IF "DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaEnclavado THEN
// Cambio desde HMI
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado := "DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaEnclavado := "DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado;
ELSIF "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaEnclavado THEN
// Cambio desde Multiplexado
"DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Orden_ConsignaEnclavado := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_DireccionSalidaByte <> "DB2006_SA".SA[#t_Index].Config_DireccionSalidaByte)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMaxIngenieria <> "DB2006_SA".SA[#t_Index].Config_ErrorMaxIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMinIngenieria <> "DB2006_SA".SA[#t_Index].Config_ErrorMinIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxIngenieria <> "DB2006_SA".SA[#t_Index].Config_EscaladoMaxIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxTarjeta <> "DB2006_SA".SA[#t_Index].Config_EscaladoMaxTarjeta)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinIngenieria <> "DB2006_SA".SA[#t_Index].Config_EscaladoMinIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinTarjeta <> "DB2006_SA".SA[#t_Index].Config_EscaladoMinTarjeta)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoOffset <> "DB2006_SA".SA[#t_Index].Config_EscaladoOffset)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_Habilitar <> "DB2006_SA".SA[#t_Index].Config_Habilitar)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMax <> "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMax)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMin <> "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMin)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_TipoAcceso <> "DB2006_SA".SA[#t_Index].Config_TipoAcceso);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_DireccionSalidaByte <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_DireccionSalidaByte)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMaxIngenieria <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMaxIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMinIngenieria <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMinIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxIngenieria <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxTarjeta <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxTarjeta)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinIngenieria <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinIngenieria)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinTarjeta <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinTarjeta)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoOffset <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoOffset)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_Habilitar <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_Habilitar)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMax <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMax)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMin <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMin)
OR ("DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_TipoAcceso <> "DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_TipoAcceso);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan := "DB2006_SA".SA[#t_Index].Estado_AutoMan;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual := "DB2006_SA".SA[#t_Index].Orden_ConsignaManual;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado := "DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_DireccionSalidaByte := "DB2006_SA".SA[#t_Index].Config_DireccionSalidaByte;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMaxIngenieria := "DB2006_SA".SA[#t_Index].Config_ErrorMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMinIngenieria := "DB2006_SA".SA[#t_Index].Config_ErrorMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxIngenieria := "DB2006_SA".SA[#t_Index].Config_EscaladoMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxTarjeta := "DB2006_SA".SA[#t_Index].Config_EscaladoMaxTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinIngenieria := "DB2006_SA".SA[#t_Index].Config_EscaladoMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinTarjeta := "DB2006_SA".SA[#t_Index].Config_EscaladoMinTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoOffset := "DB2006_SA".SA[#t_Index].Config_EscaladoOffset;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_Habilitar := "DB2006_SA".SA[#t_Index].Config_Habilitar;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMax := "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMax;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMin := "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMin;
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_TipoAcceso := "DB2006_SA".SA[#t_Index].Config_TipoAcceso;
// Actualizamos el estado original para configuraciones
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_DireccionSalidaByte := "DB2006_SA".SA[#t_Index].Config_DireccionSalidaByte;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMaxIngenieria := "DB2006_SA".SA[#t_Index].Config_ErrorMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMinIngenieria := "DB2006_SA".SA[#t_Index].Config_ErrorMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxIngenieria := "DB2006_SA".SA[#t_Index].Config_EscaladoMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxTarjeta := "DB2006_SA".SA[#t_Index].Config_EscaladoMaxTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinIngenieria := "DB2006_SA".SA[#t_Index].Config_EscaladoMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinTarjeta := "DB2006_SA".SA[#t_Index].Config_EscaladoMinTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoOffset := "DB2006_SA".SA[#t_Index].Config_EscaladoOffset;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_Habilitar := "DB2006_SA".SA[#t_Index].Config_Habilitar;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMax := "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMax;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMin := "DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMin;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_TipoAcceso := "DB2006_SA".SA[#t_Index].Config_TipoAcceso;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2006_SA".SA[#t_Index].Estado_AutoMan := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Estado_AutoMan;
"DB2006_SA".SA[#t_Index].Orden_ConsignaManual := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaManual;
"DB2006_SA".SA[#t_Index].Orden_ConsignaEnclavado := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Orden_ConsignaEnclavado;
"DB2006_SA".SA[#t_Index].Config_DireccionSalidaByte := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_DireccionSalidaByte;
"DB2006_SA".SA[#t_Index].Config_ErrorMaxIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMaxIngenieria;
"DB2006_SA".SA[#t_Index].Config_ErrorMinIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMinIngenieria;
"DB2006_SA".SA[#t_Index].Config_EscaladoMaxIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxIngenieria;
"DB2006_SA".SA[#t_Index].Config_EscaladoMaxTarjeta := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxTarjeta;
"DB2006_SA".SA[#t_Index].Config_EscaladoMinIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinIngenieria;
"DB2006_SA".SA[#t_Index].Config_EscaladoMinTarjeta := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinTarjeta;
"DB2006_SA".SA[#t_Index].Config_EscaladoOffset := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoOffset;
"DB2006_SA".SA[#t_Index].Config_Habilitar := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_Habilitar;
"DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMax := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMax;
"DB2006_SA".SA[#t_Index].Config_HabilitarAlarmaMin := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMin;
"DB2006_SA".SA[#t_Index].Config_TipoAcceso := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_TipoAcceso;
// Actualizamos el estado original para configuraciones
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_DireccionSalidaByte := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_DireccionSalidaByte;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMaxIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_ErrorMinIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_ErrorMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMaxTarjeta := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMaxTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinIngenieria := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinIngenieria;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoMinTarjeta := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoMinTarjeta;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_EscaladoOffset := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_EscaladoOffset;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_Habilitar := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_Habilitar;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMax := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMax;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_HabilitarAlarmaMin := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_HabilitarAlarmaMin;
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA.Config_TipoAcceso := "DB2806_SA_MUX".Mux.Scada[#for_mux].SA.Config_TipoAcceso;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la SA[0] que no se usa para escribir el index.
"DB2806_SA_MUX".Mux.Scada[#for_mux].SA := "DB2006_SA".SA[0];
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldSA := "DB2006_SA".SA[0];
// Actualizacion de estado anterior
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2806_SA_MUX".Mux.Scada[#for_mux].Index :=
"DB2806_SA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_FUNCTION
@@ -0,0 +1,396 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_3q6";
S7_BlockNumber := "2810";
S7_BlockTitle := "MLC_fF";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2810_ZC_V_MUX" : Void
VAR_INPUT
{ S7_MLC := "MLC_3pf" }
Arranque : Bool;
{ S7_MLC := "MLC_59s" }
SeguridadesOK : Bool;
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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
El bloque implementa la gestión de multiplexado estático para el dispositivo valvula, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_V`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2010_V.V[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `V[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2010_V` |
| UDT | `ZC_DISP_V`, `ZC_DISP_MUX_V` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2810_V_MUX".Mux.Scada[#for_mux].Index :=
"DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2810_V_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2810_V_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2810_V_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_V" THEN
"DB2810_V_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_V";
END_IF;
END_REGION LIMITES
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2810_V_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION
IF "DB2810_V_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2810_V_MUX".Mux.Scada[#for_mux].V := "DB2010_V".V[#t_Index];
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV := "DB2010_V".V[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Hmi_Estado := "DB2010_V".V[#t_Index].Hmi_Estado;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_SeguridadOk := "DB2010_V".V[#t_Index].Estado_SeguridadOk;
IF NOT "DB2010_V".V[#t_Index].Estado_AutoMan THEN
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
ELSE
IF NOT #SeguridadesOK OR NOT "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_SeguridadOk THEN
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual :=
"DB2010_V".V[#t_Index].Orden_ActivarManual := FALSE;
END_IF;
END_IF;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_Activado := "DB2010_V".V[#t_Index].Estado_Activado;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_Enclavado := "DB2010_V".V[#t_Index].Estado_Enclavado;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_Salida := "DB2010_V".V[#t_Index].Estado_Salida;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_EntradaRetornoReposo := "DB2010_V".V[#t_Index].Estado_EntradaRetornoReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_EntradaRetornoTrabajo := "DB2010_V".V[#t_Index].Estado_EntradaRetornoTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_Doble := "DB2010_V".V[#t_Index].Alarmas_Doble;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_General := "DB2010_V".V[#t_Index].Alarmas_General;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_Mantenimiento := "DB2010_V".V[#t_Index].Alarmas_Mantenimiento;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_Reposo := "DB2010_V".V[#t_Index].Alarmas_Reposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_Trabajo := "DB2010_V".V[#t_Index].Alarmas_Trabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarAuto := "DB2010_V".V[#t_Index].Orden_ActivarAuto;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_Alarma := "DB2010_V".V[#t_Index].Tiempos_Alarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_Ciclos := "DB2010_V".V[#t_Index].Tiempos_Ciclos;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Aux_oldActivado := "DB2010_V".V[#t_Index].Aux_oldActivado;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Aux_oldAlarma := "DB2010_V".V[#t_Index].Aux_oldAlarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Aux_oldAutoMan := "DB2010_V".V[#t_Index].Aux_oldAutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_GrupoAlarma := "DB2010_V".V[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2010_V".V[#t_Index].Estado_AutoMan <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan THEN
// Cambio desde HMI
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan := "DB2010_V".V[#t_Index].Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan := "DB2010_V".V[#t_Index].Estado_AutoMan;
ELSIF "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2010_V".V[#t_Index].Estado_AutoMan := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2010_V".V[#t_Index].Orden_ActivarManual <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
ELSIF "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2010_V".V[#t_Index].Orden_ActivarManual := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual;
END_IF;
// ===========================================================================================================
// Orden Reset ciclos
IF "DB2010_V".V[#t_Index].Orden_ResetCiclos <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ResetCiclos THEN
// Cambio desde HMI
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos := "DB2010_V".V[#t_Index].Orden_ResetCiclos;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ResetCiclos := "DB2010_V".V[#t_Index].Orden_ResetCiclos;
ELSIF "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ResetCiclos THEN
// Cambio desde Multiplexado
"DB2010_V".V[#t_Index].Orden_ResetCiclos := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ResetCiclos := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos := FALSE;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoBit <> "DB2010_V".V[#t_Index].Config_DireccionRetReposoBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoByte <> "DB2010_V".V[#t_Index].Config_DireccionRetReposoByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoBit <> "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoByte <> "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaBit <> "DB2010_V".V[#t_Index].Config_DireccionSalidaBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaByte <> "DB2010_V".V[#t_Index].Config_DireccionSalidaByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_Habilitar <> "DB2010_V".V[#t_Index].Config_Habilitar)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirReposo <> "DB2010_V".V[#t_Index].Config_HabilitarInvertirReposo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirTrabajo <> "DB2010_V".V[#t_Index].Config_HabilitarInvertirTrabajo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarMantenimiento <> "DB2010_V".V[#t_Index].Config_HabilitarMantenimiento)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoReposo <> "DB2010_V".V[#t_Index].Config_HabilitarRetornoReposo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoTrabajo <> "DB2010_V".V[#t_Index].Config_HabilitarRetornoTrabajo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_TipoAcceso <> "DB2010_V".V[#t_Index].Config_TipoAcceso)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointTiempoAlarma <> "DB2010_V".V[#t_Index].Tiempos_SetPointTiempoAlarma)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointCiclos <> "DB2010_V".V[#t_Index].Tiempos_SetPointCiclos);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoBit <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoByte <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoBit <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoByte <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaBit <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaByte <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_Habilitar <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_Habilitar)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirReposo <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirReposo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirTrabajo <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirTrabajo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarMantenimiento <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarMantenimiento)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoReposo <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoReposo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoTrabajo <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoTrabajo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_TipoAcceso <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_TipoAcceso)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointTiempoAlarma <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointTiempoAlarma)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointCiclos <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointCiclos);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan := "DB2010_V".V[#t_Index].Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoBit := "DB2010_V".V[#t_Index].Config_DireccionRetReposoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoByte := "DB2010_V".V[#t_Index].Config_DireccionRetReposoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoBit := "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoByte := "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaBit := "DB2010_V".V[#t_Index].Config_DireccionSalidaBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaByte := "DB2010_V".V[#t_Index].Config_DireccionSalidaByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_Habilitar := "DB2010_V".V[#t_Index].Config_Habilitar;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirReposo := "DB2010_V".V[#t_Index].Config_HabilitarInvertirReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirTrabajo := "DB2010_V".V[#t_Index].Config_HabilitarInvertirTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarMantenimiento := "DB2010_V".V[#t_Index].Config_HabilitarMantenimiento;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoReposo := "DB2010_V".V[#t_Index].Config_HabilitarRetornoReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoTrabajo := "DB2010_V".V[#t_Index].Config_HabilitarRetornoTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_TipoAcceso := "DB2010_V".V[#t_Index].Config_TipoAcceso;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointTiempoAlarma := "DB2010_V".V[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointCiclos := "DB2010_V".V[#t_Index].Tiempos_SetPointCiclos;
// Actualizamos el estado original para configuraciones
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan := "DB2010_V".V[#t_Index].Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoBit := "DB2010_V".V[#t_Index].Config_DireccionRetReposoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoByte := "DB2010_V".V[#t_Index].Config_DireccionRetReposoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoBit := "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoByte := "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaBit := "DB2010_V".V[#t_Index].Config_DireccionSalidaBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaByte := "DB2010_V".V[#t_Index].Config_DireccionSalidaByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_Habilitar := "DB2010_V".V[#t_Index].Config_Habilitar;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirReposo := "DB2010_V".V[#t_Index].Config_HabilitarInvertirReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirTrabajo := "DB2010_V".V[#t_Index].Config_HabilitarInvertirTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarMantenimiento := "DB2010_V".V[#t_Index].Config_HabilitarMantenimiento;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoReposo := "DB2010_V".V[#t_Index].Config_HabilitarRetornoReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoTrabajo := "DB2010_V".V[#t_Index].Config_HabilitarRetornoTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_TipoAcceso := "DB2010_V".V[#t_Index].Config_TipoAcceso;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointTiempoAlarma := "DB2010_V".V[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointCiclos := "DB2010_V".V[#t_Index].Tiempos_SetPointCiclos;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2010_V".V[#t_Index].Estado_AutoMan := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan;
"DB2010_V".V[#t_Index].Orden_ActivarManual := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual;
"DB2010_V".V[#t_Index].Config_DireccionRetReposoBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoBit;
"DB2010_V".V[#t_Index].Config_DireccionRetReposoByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoByte;
"DB2010_V".V[#t_Index].Config_DireccionRetTrabajoBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoBit;
"DB2010_V".V[#t_Index].Config_DireccionRetTrabajoByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoByte;
"DB2010_V".V[#t_Index].Config_DireccionSalidaBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaBit;
"DB2010_V".V[#t_Index].Config_DireccionSalidaByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaByte;
"DB2010_V".V[#t_Index].Config_Habilitar := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_Habilitar;
"DB2010_V".V[#t_Index].Config_HabilitarInvertirReposo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirReposo;
"DB2010_V".V[#t_Index].Config_HabilitarInvertirTrabajo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirTrabajo;
"DB2010_V".V[#t_Index].Config_HabilitarMantenimiento := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarMantenimiento;
"DB2010_V".V[#t_Index].Config_HabilitarRetornoReposo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoReposo;
"DB2010_V".V[#t_Index].Config_HabilitarRetornoTrabajo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoTrabajo;
"DB2010_V".V[#t_Index].Config_TipoAcceso := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_TipoAcceso;
"DB2010_V".V[#t_Index].Tiempos_SetPointTiempoAlarma := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointTiempoAlarma;
"DB2010_V".V[#t_Index].Tiempos_SetPointCiclos := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointCiclos;
// Actualizamos el estado original para configuraciones
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_Habilitar := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_Habilitar;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirReposo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirTrabajo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarMantenimiento := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarMantenimiento;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoReposo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoTrabajo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_TipoAcceso := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_TipoAcceso;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointTiempoAlarma := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointTiempoAlarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointCiclos := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointCiclos;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la V[0] que no se usa para escribir el index.
"DB2810_V_MUX".Mux.Scada[#for_mux].V := "DB2010_V".V[0];
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV := "DB2010_V".V[0];
// Actualizacion de estado anterior
"DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2810_V_MUX".Mux.Scada[#for_mux].Index :=
"DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,9 @@
MultiLingualTexts:
- id: MLC_fF
es-ES: FC2810_ZC_V_MUX
- id: MLC_3q6
es-ES: Funcion de gestion multiplexado dispositivo valvula
- id: MLC_3pf
es-ES: Primer arranque
- id: MLC_59s
es-ES: Seguridades OK
@@ -1,391 +0,0 @@
FUNCTION "FC2810_ZC_V_MUX" : Void
TITLE = FC2810_ZC_V_MUX
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion multiplexado dispositivo valvula
VAR_INPUT
Arranque : Bool; // Primer arranque
SeguridadesOK : Bool; // Seguridades OK
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
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
El bloque implementa la gestión de multiplexado estático para el dispositivo valvula, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_V`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2010_V.V[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `V[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2010_V` |
| UDT | `ZC_DISP_V`, `ZC_DISP_MUX_V` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2810_V_MUX".Mux.Scada[#for_mux].Index :=
"DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2810_V_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2810_V_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2810_V_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_V" THEN
"DB2810_V_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_V";
END_IF;
END_REGION LIMITES
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2810_V_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION
IF "DB2810_V_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2810_V_MUX".Mux.Scada[#for_mux].V := "DB2010_V".V[#t_Index];
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV := "DB2010_V".V[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Hmi_Estado := "DB2010_V".V[#t_Index].Hmi_Estado;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_SeguridadOk := "DB2010_V".V[#t_Index].Estado_SeguridadOk;
IF NOT "DB2010_V".V[#t_Index].Estado_AutoMan THEN
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
ELSE
IF NOT #SeguridadesOK OR NOT "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_SeguridadOk THEN
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual :=
"DB2010_V".V[#t_Index].Orden_ActivarManual := FALSE;
END_IF;
END_IF;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_Activado := "DB2010_V".V[#t_Index].Estado_Activado;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_Enclavado := "DB2010_V".V[#t_Index].Estado_Enclavado;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_Salida := "DB2010_V".V[#t_Index].Estado_Salida;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_EntradaRetornoReposo := "DB2010_V".V[#t_Index].Estado_EntradaRetornoReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_EntradaRetornoTrabajo := "DB2010_V".V[#t_Index].Estado_EntradaRetornoTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_Doble := "DB2010_V".V[#t_Index].Alarmas_Doble;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_General := "DB2010_V".V[#t_Index].Alarmas_General;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_Mantenimiento := "DB2010_V".V[#t_Index].Alarmas_Mantenimiento;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_Reposo := "DB2010_V".V[#t_Index].Alarmas_Reposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Alarmas_Trabajo := "DB2010_V".V[#t_Index].Alarmas_Trabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarAuto := "DB2010_V".V[#t_Index].Orden_ActivarAuto;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_Alarma := "DB2010_V".V[#t_Index].Tiempos_Alarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_Ciclos := "DB2010_V".V[#t_Index].Tiempos_Ciclos;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Aux_oldActivado := "DB2010_V".V[#t_Index].Aux_oldActivado;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Aux_oldAlarma := "DB2010_V".V[#t_Index].Aux_oldAlarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Aux_oldAutoMan := "DB2010_V".V[#t_Index].Aux_oldAutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_GrupoAlarma := "DB2010_V".V[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2010_V".V[#t_Index].Estado_AutoMan <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan THEN
// Cambio desde HMI
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan := "DB2010_V".V[#t_Index].Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan := "DB2010_V".V[#t_Index].Estado_AutoMan;
ELSIF "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2010_V".V[#t_Index].Estado_AutoMan := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2010_V".V[#t_Index].Orden_ActivarManual <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
ELSIF "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2010_V".V[#t_Index].Orden_ActivarManual := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual;
END_IF;
// ===========================================================================================================
// Orden Reset ciclos
IF "DB2010_V".V[#t_Index].Orden_ResetCiclos <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ResetCiclos THEN
// Cambio desde HMI
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos := "DB2010_V".V[#t_Index].Orden_ResetCiclos;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ResetCiclos := "DB2010_V".V[#t_Index].Orden_ResetCiclos;
ELSIF "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ResetCiclos THEN
// Cambio desde Multiplexado
"DB2010_V".V[#t_Index].Orden_ResetCiclos := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ResetCiclos := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ResetCiclos := FALSE;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoBit <> "DB2010_V".V[#t_Index].Config_DireccionRetReposoBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoByte <> "DB2010_V".V[#t_Index].Config_DireccionRetReposoByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoBit <> "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoByte <> "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaBit <> "DB2010_V".V[#t_Index].Config_DireccionSalidaBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaByte <> "DB2010_V".V[#t_Index].Config_DireccionSalidaByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_Habilitar <> "DB2010_V".V[#t_Index].Config_Habilitar)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirReposo <> "DB2010_V".V[#t_Index].Config_HabilitarInvertirReposo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirTrabajo <> "DB2010_V".V[#t_Index].Config_HabilitarInvertirTrabajo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarMantenimiento <> "DB2010_V".V[#t_Index].Config_HabilitarMantenimiento)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoReposo <> "DB2010_V".V[#t_Index].Config_HabilitarRetornoReposo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoTrabajo <> "DB2010_V".V[#t_Index].Config_HabilitarRetornoTrabajo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_TipoAcceso <> "DB2010_V".V[#t_Index].Config_TipoAcceso)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointTiempoAlarma <> "DB2010_V".V[#t_Index].Tiempos_SetPointTiempoAlarma)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointCiclos <> "DB2010_V".V[#t_Index].Tiempos_SetPointCiclos);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoBit <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoByte <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoBit <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoByte <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaBit <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaBit)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaByte <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaByte)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_Habilitar <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_Habilitar)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirReposo <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirReposo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirTrabajo <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirTrabajo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarMantenimiento <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarMantenimiento)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoReposo <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoReposo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoTrabajo <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoTrabajo)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_TipoAcceso <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_TipoAcceso)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointTiempoAlarma <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointTiempoAlarma)
OR ("DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointCiclos <> "DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointCiclos);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan := "DB2010_V".V[#t_Index].Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoBit := "DB2010_V".V[#t_Index].Config_DireccionRetReposoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoByte := "DB2010_V".V[#t_Index].Config_DireccionRetReposoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoBit := "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoByte := "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaBit := "DB2010_V".V[#t_Index].Config_DireccionSalidaBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaByte := "DB2010_V".V[#t_Index].Config_DireccionSalidaByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_Habilitar := "DB2010_V".V[#t_Index].Config_Habilitar;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirReposo := "DB2010_V".V[#t_Index].Config_HabilitarInvertirReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirTrabajo := "DB2010_V".V[#t_Index].Config_HabilitarInvertirTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarMantenimiento := "DB2010_V".V[#t_Index].Config_HabilitarMantenimiento;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoReposo := "DB2010_V".V[#t_Index].Config_HabilitarRetornoReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoTrabajo := "DB2010_V".V[#t_Index].Config_HabilitarRetornoTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_TipoAcceso := "DB2010_V".V[#t_Index].Config_TipoAcceso;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointTiempoAlarma := "DB2010_V".V[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointCiclos := "DB2010_V".V[#t_Index].Tiempos_SetPointCiclos;
// Actualizamos el estado original para configuraciones
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan := "DB2010_V".V[#t_Index].Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual := "DB2010_V".V[#t_Index].Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoBit := "DB2010_V".V[#t_Index].Config_DireccionRetReposoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoByte := "DB2010_V".V[#t_Index].Config_DireccionRetReposoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoBit := "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoByte := "DB2010_V".V[#t_Index].Config_DireccionRetTrabajoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaBit := "DB2010_V".V[#t_Index].Config_DireccionSalidaBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaByte := "DB2010_V".V[#t_Index].Config_DireccionSalidaByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_Habilitar := "DB2010_V".V[#t_Index].Config_Habilitar;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirReposo := "DB2010_V".V[#t_Index].Config_HabilitarInvertirReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirTrabajo := "DB2010_V".V[#t_Index].Config_HabilitarInvertirTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarMantenimiento := "DB2010_V".V[#t_Index].Config_HabilitarMantenimiento;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoReposo := "DB2010_V".V[#t_Index].Config_HabilitarRetornoReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoTrabajo := "DB2010_V".V[#t_Index].Config_HabilitarRetornoTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_TipoAcceso := "DB2010_V".V[#t_Index].Config_TipoAcceso;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointTiempoAlarma := "DB2010_V".V[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointCiclos := "DB2010_V".V[#t_Index].Tiempos_SetPointCiclos;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2010_V".V[#t_Index].Estado_AutoMan := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan;
"DB2010_V".V[#t_Index].Orden_ActivarManual := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual;
"DB2010_V".V[#t_Index].Config_DireccionRetReposoBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoBit;
"DB2010_V".V[#t_Index].Config_DireccionRetReposoByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoByte;
"DB2010_V".V[#t_Index].Config_DireccionRetTrabajoBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoBit;
"DB2010_V".V[#t_Index].Config_DireccionRetTrabajoByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoByte;
"DB2010_V".V[#t_Index].Config_DireccionSalidaBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaBit;
"DB2010_V".V[#t_Index].Config_DireccionSalidaByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaByte;
"DB2010_V".V[#t_Index].Config_Habilitar := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_Habilitar;
"DB2010_V".V[#t_Index].Config_HabilitarInvertirReposo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirReposo;
"DB2010_V".V[#t_Index].Config_HabilitarInvertirTrabajo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirTrabajo;
"DB2010_V".V[#t_Index].Config_HabilitarMantenimiento := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarMantenimiento;
"DB2010_V".V[#t_Index].Config_HabilitarRetornoReposo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoReposo;
"DB2010_V".V[#t_Index].Config_HabilitarRetornoTrabajo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoTrabajo;
"DB2010_V".V[#t_Index].Config_TipoAcceso := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_TipoAcceso;
"DB2010_V".V[#t_Index].Tiempos_SetPointTiempoAlarma := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointTiempoAlarma;
"DB2010_V".V[#t_Index].Tiempos_SetPointCiclos := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointCiclos;
// Actualizamos el estado original para configuraciones
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Estado_AutoMan := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Estado_AutoMan;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Orden_ActivarManual := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Orden_ActivarManual;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetReposoByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetReposoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionRetTrabajoByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionRetTrabajoByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaBit := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaBit;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_DireccionSalidaByte := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_DireccionSalidaByte;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_Habilitar := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_Habilitar;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirReposo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarInvertirTrabajo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarInvertirTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarMantenimiento := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarMantenimiento;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoReposo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoReposo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_HabilitarRetornoTrabajo := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_HabilitarRetornoTrabajo;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Config_TipoAcceso := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Config_TipoAcceso;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointTiempoAlarma := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointTiempoAlarma;
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV.Tiempos_SetPointCiclos := "DB2810_V_MUX".Mux.Scada[#for_mux].V.Tiempos_SetPointCiclos;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la V[0] que no se usa para escribir el index.
"DB2810_V_MUX".Mux.Scada[#for_mux].V := "DB2010_V".V[0];
"DB2810_V_MUX".Mux.Scada[#for_mux].oldV := "DB2010_V".V[0];
// Actualizacion de estado anterior
"DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2810_V_MUX".Mux.Scada[#for_mux].Index :=
"DB2810_V_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_FUNCTION
@@ -0,0 +1,390 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_qQ";
S7_BlockNumber := "2815";
S7_BlockTitle := "MLC_4gQ";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2815_ZC_M_MUX" : Void
VAR_INPUT
{ S7_MLC := "MLC_g3" }
Arranque : Bool;
{ S7_MLC := "MLC_4ET" }
SeguridadesOk : Bool;
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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
El bloque implementa la gestión de multiplexado estático para el dispositivo motor, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_M`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2015_M.M[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `M[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2015_M` |
| UDT | `ZC_DISP_M`, `ZC_DISP_MUX_M` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2815_M_MUX".Mux.Scada[#for_mux].Index :=
"DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2815_M_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2815_M_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2815_M_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_M" THEN
"DB2815_M_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_M";
END_IF;
END_REGION LIMITES
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2815_M_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2815_M_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2815_M_MUX".Mux.Scada[#for_mux].M := "DB2015_M".M[#t_Index];
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM := "DB2015_M".M[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Hmi_Estado := "DB2015_M".M[#t_Index].Hmi_Estado;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_SeguridadOk := "DB2015_M".M[#t_Index].Estado_SeguridadOk;
IF NOT "DB2015_M".M[#t_Index].Estado_AutoMan THEN
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
ELSE
IF NOT #SeguridadesOk OR NOT "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_SeguridadOk OR "DB2015_M".M[#t_Index].Alarmas_Termico THEN
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual :=
"DB2015_M".M[#t_Index].Orden_ActivarManual := FALSE;
END_IF;
END_IF;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_Activado := "DB2015_M".M[#t_Index].Estado_Activado;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_Enclavado := "DB2015_M".M[#t_Index].Estado_Enclavado;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_Salida := "DB2015_M".M[#t_Index].Estado_Salida;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_EntradaTermico := "DB2015_M".M[#t_Index].Estado_EntradaTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_EntradaConfMarcha := "DB2015_M".M[#t_Index].Estado_EntradaConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Alarmas_ConfirmacionMarcha := "DB2015_M".M[#t_Index].Alarmas_ConfirmacionMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Alarmas_General := "DB2015_M".M[#t_Index].Alarmas_General;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Alarmas_Mantenimiento := "DB2015_M".M[#t_Index].Alarmas_Mantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Alarmas_Termico := "DB2015_M".M[#t_Index].Alarmas_Termico;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarAuto := "DB2015_M".M[#t_Index].Orden_ActivarAuto;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_Alarma := "DB2015_M".M[#t_Index].Tiempos_Alarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_Mantenimiento := "DB2015_M".M[#t_Index].Tiempos_Mantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Aux_oldActivado := "DB2015_M".M[#t_Index].Aux_oldActivado;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Aux_oldAlarma := "DB2015_M".M[#t_Index].Aux_oldAlarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Aux_oldAutoMan := "DB2015_M".M[#t_Index].Aux_oldAutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_GrupoAlarma := "DB2015_M".M[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2015_M".M[#t_Index].Estado_AutoMan <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan THEN
// Cambio desde HMI
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan := "DB2015_M".M[#t_Index].Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan := "DB2015_M".M[#t_Index].Estado_AutoMan;
ELSIF "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2015_M".M[#t_Index].Estado_AutoMan := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2015_M".M[#t_Index].Orden_ActivarManual <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
ELSIF "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2015_M".M[#t_Index].Orden_ActivarManual := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual;
END_IF;
// ===========================================================================================================
// Orden Reset horas
IF "DB2015_M".M[#t_Index].Orden_ResetHoras <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ResetHoras THEN
// Cambio desde HMI
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras := "DB2015_M".M[#t_Index].Orden_ResetHoras;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ResetHoras := "DB2015_M".M[#t_Index].Orden_ResetHoras;
ELSIF "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ResetHoras THEN
// Cambio desde Multiplexado
"DB2015_M".M[#t_Index].Orden_ResetHoras := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ResetHoras := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras := FALSE;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaBit <> "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaByte <> "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoBit <> "DB2015_M".M[#t_Index].Config_DireccionRetTermicoBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoByte <> "DB2015_M".M[#t_Index].Config_DireccionRetTermicoByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaBit <> "DB2015_M".M[#t_Index].Config_DireccionSalidaBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaByte <> "DB2015_M".M[#t_Index].Config_DireccionSalidaByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_Habilitar <> "DB2015_M".M[#t_Index].Config_Habilitar)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirConfMarcha <> "DB2015_M".M[#t_Index].Config_HabilitarInvertirConfMarcha)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirTermico <> "DB2015_M".M[#t_Index].Config_HabilitarInvertirTermico)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarMantenimiento <> "DB2015_M".M[#t_Index].Config_HabilitarMantenimiento)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoConfMarcha <> "DB2015_M".M[#t_Index].Config_HabilitarRetornoConfMarcha)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoTermico <> "DB2015_M".M[#t_Index].Config_HabilitarRetornoTermico)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_TipoAcceso <> "DB2015_M".M[#t_Index].Config_TipoAcceso)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointTiempoAlarma <> "DB2015_M".M[#t_Index].Tiempos_SetPointTiempoAlarma)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointMantenimiento <> "DB2015_M".M[#t_Index].Tiempos_SetPointMantenimiento);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaBit <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaByte <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoBit <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoByte <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaBit <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaByte <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_Habilitar <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_Habilitar)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirConfMarcha <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirConfMarcha)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirTermico <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirTermico)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarMantenimiento <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarMantenimiento)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoConfMarcha <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoConfMarcha)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoTermico <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoTermico)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_TipoAcceso <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_TipoAcceso)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointTiempoAlarma <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointTiempoAlarma)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointMantenimiento <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointMantenimiento);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan := "DB2015_M".M[#t_Index].Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaBit := "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaByte := "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoBit := "DB2015_M".M[#t_Index].Config_DireccionRetTermicoBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoByte := "DB2015_M".M[#t_Index].Config_DireccionRetTermicoByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaBit := "DB2015_M".M[#t_Index].Config_DireccionSalidaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaByte := "DB2015_M".M[#t_Index].Config_DireccionSalidaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_Habilitar := "DB2015_M".M[#t_Index].Config_Habilitar;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirConfMarcha := "DB2015_M".M[#t_Index].Config_HabilitarInvertirConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirTermico := "DB2015_M".M[#t_Index].Config_HabilitarInvertirTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarMantenimiento := "DB2015_M".M[#t_Index].Config_HabilitarMantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoConfMarcha := "DB2015_M".M[#t_Index].Config_HabilitarRetornoConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoTermico := "DB2015_M".M[#t_Index].Config_HabilitarRetornoTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_TipoAcceso := "DB2015_M".M[#t_Index].Config_TipoAcceso;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointTiempoAlarma := "DB2015_M".M[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointMantenimiento := "DB2015_M".M[#t_Index].Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan := "DB2015_M".M[#t_Index].Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaBit := "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaByte := "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoBit := "DB2015_M".M[#t_Index].Config_DireccionRetTermicoBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoByte := "DB2015_M".M[#t_Index].Config_DireccionRetTermicoByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaBit := "DB2015_M".M[#t_Index].Config_DireccionSalidaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaByte := "DB2015_M".M[#t_Index].Config_DireccionSalidaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_Habilitar := "DB2015_M".M[#t_Index].Config_Habilitar;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirConfMarcha := "DB2015_M".M[#t_Index].Config_HabilitarInvertirConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirTermico := "DB2015_M".M[#t_Index].Config_HabilitarInvertirTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarMantenimiento := "DB2015_M".M[#t_Index].Config_HabilitarMantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoConfMarcha := "DB2015_M".M[#t_Index].Config_HabilitarRetornoConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoTermico := "DB2015_M".M[#t_Index].Config_HabilitarRetornoTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_TipoAcceso := "DB2015_M".M[#t_Index].Config_TipoAcceso;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointTiempoAlarma := "DB2015_M".M[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointMantenimiento := "DB2015_M".M[#t_Index].Tiempos_SetPointMantenimiento;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2015_M".M[#t_Index].Estado_AutoMan := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan;
"DB2015_M".M[#t_Index].Orden_ActivarManual := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual;
"DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaBit;
"DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaByte;
"DB2015_M".M[#t_Index].Config_DireccionRetTermicoBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoBit;
"DB2015_M".M[#t_Index].Config_DireccionRetTermicoByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoByte;
"DB2015_M".M[#t_Index].Config_DireccionSalidaBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaBit;
"DB2015_M".M[#t_Index].Config_DireccionSalidaByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaByte;
"DB2015_M".M[#t_Index].Config_Habilitar := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_Habilitar;
"DB2015_M".M[#t_Index].Config_HabilitarInvertirConfMarcha := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirConfMarcha;
"DB2015_M".M[#t_Index].Config_HabilitarInvertirTermico := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirTermico;
"DB2015_M".M[#t_Index].Config_HabilitarMantenimiento := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarMantenimiento;
"DB2015_M".M[#t_Index].Config_HabilitarRetornoConfMarcha := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoConfMarcha;
"DB2015_M".M[#t_Index].Config_HabilitarRetornoTermico := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoTermico;
"DB2015_M".M[#t_Index].Config_TipoAcceso := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_TipoAcceso;
"DB2015_M".M[#t_Index].Tiempos_SetPointTiempoAlarma := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointTiempoAlarma;
"DB2015_M".M[#t_Index].Tiempos_SetPointMantenimiento := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_Habilitar := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_Habilitar;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirConfMarcha := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirTermico := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarMantenimiento := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarMantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoConfMarcha := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoTermico := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_TipoAcceso := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_TipoAcceso;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointTiempoAlarma := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointTiempoAlarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointMantenimiento := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointMantenimiento;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la M[0] que no se usa para escribir el index.
"DB2815_M_MUX".Mux.Scada[#for_mux].M := "DB2015_M".M[0];
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM := "DB2015_M".M[0];
// Actualizacion de estado anterior
"DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2815_M_MUX".Mux.Scada[#for_mux].Index :=
"DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,9 @@
MultiLingualTexts:
- id: MLC_4gQ
es-ES: FC2815_ZC_M_MUX
- id: MLC_qQ
es-ES: Funcion de gestion multiplexado dispositivo Motor
- id: MLC_g3
es-ES: Primer arranque
- id: MLC_4ET
es-ES: Seguridades OK
@@ -1,385 +0,0 @@
FUNCTION "FC2815_ZC_M_MUX" : Void
TITLE = FC2815_ZC_M_MUX
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion multiplexado dispositivo Motor
VAR_INPUT
Arranque : Bool; // Primer arranque
SeguridadesOk : Bool; // Seguridades OK
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
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
El bloque implementa la gestión de multiplexado estático para el dispositivo motor, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_M`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2015_M.M[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `M[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2015_M` |
| UDT | `ZC_DISP_M`, `ZC_DISP_MUX_M` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2815_M_MUX".Mux.Scada[#for_mux].Index :=
"DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2815_M_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2815_M_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2815_M_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_M" THEN
"DB2815_M_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_M";
END_IF;
END_REGION LIMITES
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2815_M_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2815_M_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2815_M_MUX".Mux.Scada[#for_mux].M := "DB2015_M".M[#t_Index];
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM := "DB2015_M".M[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Hmi_Estado := "DB2015_M".M[#t_Index].Hmi_Estado;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_SeguridadOk := "DB2015_M".M[#t_Index].Estado_SeguridadOk;
IF NOT "DB2015_M".M[#t_Index].Estado_AutoMan THEN
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
ELSE
IF NOT #SeguridadesOk OR NOT "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_SeguridadOk OR "DB2015_M".M[#t_Index].Alarmas_Termico THEN
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual :=
"DB2015_M".M[#t_Index].Orden_ActivarManual := FALSE;
END_IF;
END_IF;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_Activado := "DB2015_M".M[#t_Index].Estado_Activado;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_Enclavado := "DB2015_M".M[#t_Index].Estado_Enclavado;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_Salida := "DB2015_M".M[#t_Index].Estado_Salida;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_EntradaTermico := "DB2015_M".M[#t_Index].Estado_EntradaTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_EntradaConfMarcha := "DB2015_M".M[#t_Index].Estado_EntradaConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Alarmas_ConfirmacionMarcha := "DB2015_M".M[#t_Index].Alarmas_ConfirmacionMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Alarmas_General := "DB2015_M".M[#t_Index].Alarmas_General;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Alarmas_Mantenimiento := "DB2015_M".M[#t_Index].Alarmas_Mantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Alarmas_Termico := "DB2015_M".M[#t_Index].Alarmas_Termico;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarAuto := "DB2015_M".M[#t_Index].Orden_ActivarAuto;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_Alarma := "DB2015_M".M[#t_Index].Tiempos_Alarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_Mantenimiento := "DB2015_M".M[#t_Index].Tiempos_Mantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Aux_oldActivado := "DB2015_M".M[#t_Index].Aux_oldActivado;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Aux_oldAlarma := "DB2015_M".M[#t_Index].Aux_oldAlarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Aux_oldAutoMan := "DB2015_M".M[#t_Index].Aux_oldAutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_GrupoAlarma := "DB2015_M".M[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2015_M".M[#t_Index].Estado_AutoMan <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan THEN
// Cambio desde HMI
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan := "DB2015_M".M[#t_Index].Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan := "DB2015_M".M[#t_Index].Estado_AutoMan;
ELSIF "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2015_M".M[#t_Index].Estado_AutoMan := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2015_M".M[#t_Index].Orden_ActivarManual <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
ELSIF "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2015_M".M[#t_Index].Orden_ActivarManual := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual;
END_IF;
// ===========================================================================================================
// Orden Reset horas
IF "DB2015_M".M[#t_Index].Orden_ResetHoras <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ResetHoras THEN
// Cambio desde HMI
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras := "DB2015_M".M[#t_Index].Orden_ResetHoras;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ResetHoras := "DB2015_M".M[#t_Index].Orden_ResetHoras;
ELSIF "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ResetHoras THEN
// Cambio desde Multiplexado
"DB2015_M".M[#t_Index].Orden_ResetHoras := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ResetHoras := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ResetHoras := FALSE;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaBit <> "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaByte <> "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoBit <> "DB2015_M".M[#t_Index].Config_DireccionRetTermicoBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoByte <> "DB2015_M".M[#t_Index].Config_DireccionRetTermicoByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaBit <> "DB2015_M".M[#t_Index].Config_DireccionSalidaBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaByte <> "DB2015_M".M[#t_Index].Config_DireccionSalidaByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_Habilitar <> "DB2015_M".M[#t_Index].Config_Habilitar)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirConfMarcha <> "DB2015_M".M[#t_Index].Config_HabilitarInvertirConfMarcha)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirTermico <> "DB2015_M".M[#t_Index].Config_HabilitarInvertirTermico)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarMantenimiento <> "DB2015_M".M[#t_Index].Config_HabilitarMantenimiento)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoConfMarcha <> "DB2015_M".M[#t_Index].Config_HabilitarRetornoConfMarcha)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoTermico <> "DB2015_M".M[#t_Index].Config_HabilitarRetornoTermico)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_TipoAcceso <> "DB2015_M".M[#t_Index].Config_TipoAcceso)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointTiempoAlarma <> "DB2015_M".M[#t_Index].Tiempos_SetPointTiempoAlarma)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointMantenimiento <> "DB2015_M".M[#t_Index].Tiempos_SetPointMantenimiento);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaBit <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaByte <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoBit <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoByte <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaBit <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaBit)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaByte <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaByte)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_Habilitar <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_Habilitar)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirConfMarcha <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirConfMarcha)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirTermico <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirTermico)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarMantenimiento <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarMantenimiento)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoConfMarcha <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoConfMarcha)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoTermico <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoTermico)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_TipoAcceso <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_TipoAcceso)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointTiempoAlarma <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointTiempoAlarma)
OR ("DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointMantenimiento <> "DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointMantenimiento);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan := "DB2015_M".M[#t_Index].Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaBit := "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaByte := "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoBit := "DB2015_M".M[#t_Index].Config_DireccionRetTermicoBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoByte := "DB2015_M".M[#t_Index].Config_DireccionRetTermicoByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaBit := "DB2015_M".M[#t_Index].Config_DireccionSalidaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaByte := "DB2015_M".M[#t_Index].Config_DireccionSalidaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_Habilitar := "DB2015_M".M[#t_Index].Config_Habilitar;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirConfMarcha := "DB2015_M".M[#t_Index].Config_HabilitarInvertirConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirTermico := "DB2015_M".M[#t_Index].Config_HabilitarInvertirTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarMantenimiento := "DB2015_M".M[#t_Index].Config_HabilitarMantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoConfMarcha := "DB2015_M".M[#t_Index].Config_HabilitarRetornoConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoTermico := "DB2015_M".M[#t_Index].Config_HabilitarRetornoTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_TipoAcceso := "DB2015_M".M[#t_Index].Config_TipoAcceso;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointTiempoAlarma := "DB2015_M".M[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointMantenimiento := "DB2015_M".M[#t_Index].Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan := "DB2015_M".M[#t_Index].Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual := "DB2015_M".M[#t_Index].Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaBit := "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaByte := "DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoBit := "DB2015_M".M[#t_Index].Config_DireccionRetTermicoBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoByte := "DB2015_M".M[#t_Index].Config_DireccionRetTermicoByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaBit := "DB2015_M".M[#t_Index].Config_DireccionSalidaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaByte := "DB2015_M".M[#t_Index].Config_DireccionSalidaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_Habilitar := "DB2015_M".M[#t_Index].Config_Habilitar;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirConfMarcha := "DB2015_M".M[#t_Index].Config_HabilitarInvertirConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirTermico := "DB2015_M".M[#t_Index].Config_HabilitarInvertirTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarMantenimiento := "DB2015_M".M[#t_Index].Config_HabilitarMantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoConfMarcha := "DB2015_M".M[#t_Index].Config_HabilitarRetornoConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoTermico := "DB2015_M".M[#t_Index].Config_HabilitarRetornoTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_TipoAcceso := "DB2015_M".M[#t_Index].Config_TipoAcceso;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointTiempoAlarma := "DB2015_M".M[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointMantenimiento := "DB2015_M".M[#t_Index].Tiempos_SetPointMantenimiento;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2015_M".M[#t_Index].Estado_AutoMan := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan;
"DB2015_M".M[#t_Index].Orden_ActivarManual := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual;
"DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaBit;
"DB2015_M".M[#t_Index].Config_DireccionRetConfMarchaByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaByte;
"DB2015_M".M[#t_Index].Config_DireccionRetTermicoBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoBit;
"DB2015_M".M[#t_Index].Config_DireccionRetTermicoByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoByte;
"DB2015_M".M[#t_Index].Config_DireccionSalidaBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaBit;
"DB2015_M".M[#t_Index].Config_DireccionSalidaByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaByte;
"DB2015_M".M[#t_Index].Config_Habilitar := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_Habilitar;
"DB2015_M".M[#t_Index].Config_HabilitarInvertirConfMarcha := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirConfMarcha;
"DB2015_M".M[#t_Index].Config_HabilitarInvertirTermico := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirTermico;
"DB2015_M".M[#t_Index].Config_HabilitarMantenimiento := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarMantenimiento;
"DB2015_M".M[#t_Index].Config_HabilitarRetornoConfMarcha := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoConfMarcha;
"DB2015_M".M[#t_Index].Config_HabilitarRetornoTermico := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoTermico;
"DB2015_M".M[#t_Index].Config_TipoAcceso := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_TipoAcceso;
"DB2015_M".M[#t_Index].Tiempos_SetPointTiempoAlarma := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointTiempoAlarma;
"DB2015_M".M[#t_Index].Tiempos_SetPointMantenimiento := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Estado_AutoMan := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Estado_AutoMan;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Orden_ActivarManual := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Orden_ActivarManual;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetConfMarchaByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetConfMarchaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionRetTermicoByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionRetTermicoByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaBit := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaBit;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_DireccionSalidaByte := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_DireccionSalidaByte;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_Habilitar := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_Habilitar;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirConfMarcha := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarInvertirTermico := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarInvertirTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarMantenimiento := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarMantenimiento;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoConfMarcha := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoConfMarcha;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_HabilitarRetornoTermico := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_HabilitarRetornoTermico;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Config_TipoAcceso := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Config_TipoAcceso;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointTiempoAlarma := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointTiempoAlarma;
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM.Tiempos_SetPointMantenimiento := "DB2815_M_MUX".Mux.Scada[#for_mux].M.Tiempos_SetPointMantenimiento;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la M[0] que no se usa para escribir el index.
"DB2815_M_MUX".Mux.Scada[#for_mux].M := "DB2015_M".M[0];
"DB2815_M_MUX".Mux.Scada[#for_mux].oldM := "DB2015_M".M[0];
// Actualizacion de estado anterior
"DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2815_M_MUX".Mux.Scada[#for_mux].Index :=
"DB2815_M_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_FUNCTION
@@ -0,0 +1,437 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_4db";
S7_BlockNumber := "2816";
S7_BlockTitle := "MLC_44x";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2816_ZC_M_VF_MUX" : Void
VAR_INPUT
{ S7_MLC := "MLC_4gs" }
Arranque : Bool;
{ S7_MLC := "MLC_4eV" }
SeguridadesOK : Bool;
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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
El bloque implementa la gestión de multiplexado estático para el dispositivo motor variador, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_M_VF`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2016_M_VF.M_VF[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `M_VF[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2016_M_VF` |
| UDT | `ZC_DISP_M_VF`, `ZC_DISP_MUX_M_VF` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index :=
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_M_VF" THEN
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_M_VF";
END_IF;
END_REGION LIMITES
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF := "DB2016_M_VF".M_VF[#t_Index];
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF := "DB2016_M_VF".M_VF[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Hmi_Estado := "DB2016_M_VF".M_VF[#t_Index].Hmi_Estado;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_SeguridadOk := "DB2016_M_VF".M_VF[#t_Index].Estado_SeguridadOk;
IF NOT "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan AND #SeguridadesOK AND "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_SeguridadOk AND NOT "DB2016_M_VF".M_VF[#t_Index].Alarmas_Termico THEN
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
ELSE
IF NOT #SeguridadesOK OR NOT "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_SeguridadOk OR "DB2016_M_VF".M_VF[#t_Index].Alarmas_Termico THEN
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual :=
"DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual := FALSE;
END_IF;
END_IF;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_Activado := "DB2016_M_VF".M_VF[#t_Index].Estado_Activado;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_Enclavado := "DB2016_M_VF".M_VF[#t_Index].Estado_Enclavado;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_Salida := "DB2016_M_VF".M_VF[#t_Index].Estado_Salida;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_EntradaTermico := "DB2016_M_VF".M_VF[#t_Index].Estado_EntradaTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_EntradaConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Estado_EntradaConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_ValorActual := "DB2016_M_VF".M_VF[#t_Index].Estado_ValorActual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_ValorTarjeta := "DB2016_M_VF".M_VF[#t_Index].Estado_ValorTarjeta;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_Escritura := "DB2016_M_VF".M_VF[#t_Index].Estado_Escritura;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_ConfirmacionMarcha := "DB2016_M_VF".M_VF[#t_Index].Alarmas_ConfirmacionMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_Escritura := "DB2016_M_VF".M_VF[#t_Index].Alarmas_Escritura;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_General := "DB2016_M_VF".M_VF[#t_Index].Alarmas_General;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_Mantenimiento := "DB2016_M_VF".M_VF[#t_Index].Alarmas_Mantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_Parametros := "DB2016_M_VF".M_VF[#t_Index].Alarmas_Parametros;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_Termico := "DB2016_M_VF".M_VF[#t_Index].Alarmas_Termico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarAuto := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarAuto;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaAuto := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaAuto;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_Alarma := "DB2016_M_VF".M_VF[#t_Index].Tiempos_Alarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_Mantenimiento := "DB2016_M_VF".M_VF[#t_Index].Tiempos_Mantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Aux_oldActivado := "DB2016_M_VF".M_VF[#t_Index].Aux_oldActivado;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Aux_oldAlarma := "DB2016_M_VF".M_VF[#t_Index].Aux_oldAlarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Aux_oldAutoMan := "DB2016_M_VF".M_VF[#t_Index].Aux_oldAutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_GrupoAlarma := "DB2016_M_VF".M_VF[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan THEN
// Cambio desde HMI
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan := "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan := "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan;
ELSIF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
ELSIF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual;
END_IF;
// ===========================================================================================================
// Orden Consigna Manual
IF "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual THEN
// Cambio desde HMI
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
ELSIF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual THEN
// Cambio desde Multiplexado
"DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual;
END_IF;
// ===========================================================================================================
// Orden Reset horas
IF "DB2016_M_VF".M_VF[#t_Index].Orden_ResetHoras <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ResetHoras THEN
// Cambio desde HMI
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras := "DB2016_M_VF".M_VF[#t_Index].Orden_ResetHoras;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ResetHoras := "DB2016_M_VF".M_VF[#t_Index].Orden_ResetHoras;
ELSIF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ResetHoras THEN
// Cambio desde Multiplexado
"DB2016_M_VF".M_VF[#t_Index].Orden_ResetHoras := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ResetHoras := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras := FALSE;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaBit <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaByte <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoBit <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoByte <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaAnalogicaByte <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaAnalogicaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaBit <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaByte <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMaxIngenieria <> "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMaxIngenieria)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMinIngenieria <> "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMinIngenieria)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_Habilitar <> "DB2016_M_VF".M_VF[#t_Index].Config_Habilitar)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarAlarmaEscrituraSalida <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarAlarmaEscrituraSalida)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirConfMarcha <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirConfMarcha)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirTermico <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirTermico)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarMantenimiento <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarMantenimiento)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoConfMarcha <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoConfMarcha)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoTermico <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoTermico)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_TipoAcceso <> "DB2016_M_VF".M_VF[#t_Index].Config_TipoAcceso)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointAlarma <> "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointAlarma)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointMantenimiento <> "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointMantenimiento);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaBit <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaByte <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoBit <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoByte <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaAnalogicaByte <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaAnalogicaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaBit <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaByte <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMaxIngenieria <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMaxIngenieria)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMinIngenieria <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMinIngenieria)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_Habilitar <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_Habilitar)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarAlarmaEscrituraSalida <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarAlarmaEscrituraSalida)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirConfMarcha <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirConfMarcha)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirTermico <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirTermico)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarMantenimiento <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarMantenimiento)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoConfMarcha <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoConfMarcha)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoTermico <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoTermico)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_TipoAcceso <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_TipoAcceso)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointAlarma <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointAlarma)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointMantenimiento <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointMantenimiento);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan := "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaAnalogicaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaAnalogicaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMaxIngenieria := "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMaxIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMinIngenieria := "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMinIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_Habilitar := "DB2016_M_VF".M_VF[#t_Index].Config_Habilitar;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarAlarmaEscrituraSalida := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarAlarmaEscrituraSalida;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirTermico := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarMantenimiento := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarMantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoTermico := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_TipoAcceso := "DB2016_M_VF".M_VF[#t_Index].Config_TipoAcceso;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointAlarma := "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointAlarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointMantenimiento := "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan := "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaAnalogicaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaAnalogicaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMaxIngenieria := "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMaxIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMinIngenieria := "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMinIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_Habilitar := "DB2016_M_VF".M_VF[#t_Index].Config_Habilitar;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarAlarmaEscrituraSalida := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarAlarmaEscrituraSalida;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirTermico := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarMantenimiento := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarMantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoTermico := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_TipoAcceso := "DB2016_M_VF".M_VF[#t_Index].Config_TipoAcceso;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointAlarma := "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointAlarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointMantenimiento := "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointMantenimiento;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan;
"DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual;
"DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaBit;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaByte;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoBit;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoByte;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaAnalogicaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaAnalogicaByte;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaBit;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaByte;
"DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMaxIngenieria := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMaxIngenieria;
"DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMinIngenieria := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMinIngenieria;
"DB2016_M_VF".M_VF[#t_Index].Config_Habilitar := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_Habilitar;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarAlarmaEscrituraSalida := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarAlarmaEscrituraSalida;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirConfMarcha := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirConfMarcha;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirTermico := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirTermico;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarMantenimiento := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarMantenimiento;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoConfMarcha := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoConfMarcha;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoTermico := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoTermico;
"DB2016_M_VF".M_VF[#t_Index].Config_TipoAcceso := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_TipoAcceso;
"DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointAlarma := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointAlarma;
"DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointMantenimiento := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaAnalogicaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaAnalogicaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMaxIngenieria := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMaxIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMinIngenieria := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMinIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_Habilitar := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_Habilitar;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarAlarmaEscrituraSalida := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarAlarmaEscrituraSalida;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirConfMarcha := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirTermico := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarMantenimiento := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarMantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoConfMarcha := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoTermico := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_TipoAcceso := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_TipoAcceso;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointAlarma := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointAlarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointMantenimiento := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointMantenimiento;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la M_VF[0] que no se usa para escribir el index.
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF := "DB2016_M_VF".M_VF[0];
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF := "DB2016_M_VF".M_VF[0];
// Actualizacion de estado anterior
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index :=
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_NETWORK
END_FUNCTION
@@ -0,0 +1,9 @@
MultiLingualTexts:
- id: MLC_44x
es-ES: FC2816_ZC_M_VF_MUX
- id: MLC_4db
es-ES: Funcion de gestion multiplexado dispositivo Motor variador
- id: MLC_4gs
es-ES: Primer arranque
- id: MLC_4eV
es-ES: Seguridades OK
@@ -1,432 +0,0 @@
FUNCTION "FC2816_ZC_M_VF_MUX" : Void
TITLE = FC2816_ZC_M_VF_MUX
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion multiplexado dispositivo Motor variador
VAR_INPUT
Arranque : Bool; // Primer arranque
SeguridadesOK : Bool; // Seguridades OK
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
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
El bloque implementa la gestión de multiplexado estático para el dispositivo motor variador, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_M_VF`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2016_M_VF.M_VF[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `M_VF[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2016_M_VF` |
| UDT | `ZC_DISP_M_VF`, `ZC_DISP_MUX_M_VF` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index :=
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_M_VF" THEN
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_M_VF";
END_IF;
END_REGION LIMITES
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_Index <> #t_oldIndex THEN
// Traspaso de dispositivos al multiplexado
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF := "DB2016_M_VF".M_VF[#t_Index];
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF := "DB2016_M_VF".M_VF[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION CARGA_INICIAL
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Hmi_Estado := "DB2016_M_VF".M_VF[#t_Index].Hmi_Estado;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_SeguridadOk := "DB2016_M_VF".M_VF[#t_Index].Estado_SeguridadOk;
IF NOT "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan AND #SeguridadesOK AND "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_SeguridadOk AND NOT "DB2016_M_VF".M_VF[#t_Index].Alarmas_Termico THEN
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
ELSE
IF NOT #SeguridadesOK OR NOT "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_SeguridadOk OR "DB2016_M_VF".M_VF[#t_Index].Alarmas_Termico THEN
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual :=
"DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual := FALSE;
END_IF;
END_IF;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_Activado := "DB2016_M_VF".M_VF[#t_Index].Estado_Activado;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_Enclavado := "DB2016_M_VF".M_VF[#t_Index].Estado_Enclavado;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_Salida := "DB2016_M_VF".M_VF[#t_Index].Estado_Salida;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_EntradaTermico := "DB2016_M_VF".M_VF[#t_Index].Estado_EntradaTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_EntradaConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Estado_EntradaConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_ValorActual := "DB2016_M_VF".M_VF[#t_Index].Estado_ValorActual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_ValorTarjeta := "DB2016_M_VF".M_VF[#t_Index].Estado_ValorTarjeta;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_Escritura := "DB2016_M_VF".M_VF[#t_Index].Estado_Escritura;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_ConfirmacionMarcha := "DB2016_M_VF".M_VF[#t_Index].Alarmas_ConfirmacionMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_Escritura := "DB2016_M_VF".M_VF[#t_Index].Alarmas_Escritura;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_General := "DB2016_M_VF".M_VF[#t_Index].Alarmas_General;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_Mantenimiento := "DB2016_M_VF".M_VF[#t_Index].Alarmas_Mantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_Parametros := "DB2016_M_VF".M_VF[#t_Index].Alarmas_Parametros;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Alarmas_Termico := "DB2016_M_VF".M_VF[#t_Index].Alarmas_Termico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarAuto := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarAuto;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaAuto := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaAuto;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_Alarma := "DB2016_M_VF".M_VF[#t_Index].Tiempos_Alarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_Mantenimiento := "DB2016_M_VF".M_VF[#t_Index].Tiempos_Mantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Aux_oldActivado := "DB2016_M_VF".M_VF[#t_Index].Aux_oldActivado;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Aux_oldAlarma := "DB2016_M_VF".M_VF[#t_Index].Aux_oldAlarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Aux_oldAutoMan := "DB2016_M_VF".M_VF[#t_Index].Aux_oldAutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_GrupoAlarma := "DB2016_M_VF".M_VF[#t_Index].Config_GrupoAlarma;
END_REGION DATOS_LECTURA
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan THEN
// Cambio desde HMI
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan := "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan := "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan;
ELSIF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
ELSIF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual;
END_IF;
// ===========================================================================================================
// Orden Consigna Manual
IF "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual THEN
// Cambio desde HMI
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
ELSIF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual THEN
// Cambio desde Multiplexado
"DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual;
END_IF;
// ===========================================================================================================
// Orden Reset horas
IF "DB2016_M_VF".M_VF[#t_Index].Orden_ResetHoras <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ResetHoras THEN
// Cambio desde HMI
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras := "DB2016_M_VF".M_VF[#t_Index].Orden_ResetHoras;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ResetHoras := "DB2016_M_VF".M_VF[#t_Index].Orden_ResetHoras;
ELSIF "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ResetHoras THEN
// Cambio desde Multiplexado
"DB2016_M_VF".M_VF[#t_Index].Orden_ResetHoras := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ResetHoras := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ResetHoras := FALSE;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaBit <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaByte <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoBit <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoByte <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaAnalogicaByte <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaAnalogicaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaBit <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaByte <> "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMaxIngenieria <> "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMaxIngenieria)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMinIngenieria <> "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMinIngenieria)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_Habilitar <> "DB2016_M_VF".M_VF[#t_Index].Config_Habilitar)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarAlarmaEscrituraSalida <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarAlarmaEscrituraSalida)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirConfMarcha <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirConfMarcha)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirTermico <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirTermico)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarMantenimiento <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarMantenimiento)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoConfMarcha <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoConfMarcha)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoTermico <> "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoTermico)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_TipoAcceso <> "DB2016_M_VF".M_VF[#t_Index].Config_TipoAcceso)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointAlarma <> "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointAlarma)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointMantenimiento <> "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointMantenimiento);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaBit <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaByte <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoBit <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoByte <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaAnalogicaByte <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaAnalogicaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaBit <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaBit)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaByte <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaByte)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMaxIngenieria <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMaxIngenieria)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMinIngenieria <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMinIngenieria)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_Habilitar <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_Habilitar)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarAlarmaEscrituraSalida <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarAlarmaEscrituraSalida)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirConfMarcha <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirConfMarcha)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirTermico <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirTermico)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarMantenimiento <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarMantenimiento)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoConfMarcha <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoConfMarcha)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoTermico <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoTermico)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_TipoAcceso <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_TipoAcceso)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointAlarma <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointAlarma)
OR ("DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointMantenimiento <> "DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointMantenimiento);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan := "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaAnalogicaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaAnalogicaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMaxIngenieria := "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMaxIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMinIngenieria := "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMinIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_Habilitar := "DB2016_M_VF".M_VF[#t_Index].Config_Habilitar;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarAlarmaEscrituraSalida := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarAlarmaEscrituraSalida;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirTermico := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarMantenimiento := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarMantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoTermico := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_TipoAcceso := "DB2016_M_VF".M_VF[#t_Index].Config_TipoAcceso;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointAlarma := "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointAlarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointMantenimiento := "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan := "DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual := "DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaAnalogicaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaAnalogicaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaBit := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaByte := "DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMaxIngenieria := "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMaxIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMinIngenieria := "DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMinIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_Habilitar := "DB2016_M_VF".M_VF[#t_Index].Config_Habilitar;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarAlarmaEscrituraSalida := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarAlarmaEscrituraSalida;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirTermico := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarMantenimiento := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarMantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoConfMarcha := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoTermico := "DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_TipoAcceso := "DB2016_M_VF".M_VF[#t_Index].Config_TipoAcceso;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointAlarma := "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointAlarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointMantenimiento := "DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointMantenimiento;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2016_M_VF".M_VF[#t_Index].Estado_AutoMan := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan;
"DB2016_M_VF".M_VF[#t_Index].Orden_ActivarManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual;
"DB2016_M_VF".M_VF[#t_Index].Orden_ConsignaManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaBit;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetConfMarchaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaByte;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoBit;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionRetTermicoByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoByte;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaAnalogicaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaAnalogicaByte;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaBit;
"DB2016_M_VF".M_VF[#t_Index].Config_DireccionSalidaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaByte;
"DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMaxIngenieria := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMaxIngenieria;
"DB2016_M_VF".M_VF[#t_Index].Config_EscaladoMinIngenieria := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMinIngenieria;
"DB2016_M_VF".M_VF[#t_Index].Config_Habilitar := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_Habilitar;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarAlarmaEscrituraSalida := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarAlarmaEscrituraSalida;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirConfMarcha := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirConfMarcha;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarInvertirTermico := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirTermico;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarMantenimiento := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarMantenimiento;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoConfMarcha := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoConfMarcha;
"DB2016_M_VF".M_VF[#t_Index].Config_HabilitarRetornoTermico := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoTermico;
"DB2016_M_VF".M_VF[#t_Index].Config_TipoAcceso := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_TipoAcceso;
"DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointAlarma := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointAlarma;
"DB2016_M_VF".M_VF[#t_Index].Tiempos_SetPointMantenimiento := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Estado_AutoMan := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Estado_AutoMan;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ActivarManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ActivarManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Orden_ConsignaManual := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Orden_ConsignaManual;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetConfMarchaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetConfMarchaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionRetTermicoByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionRetTermicoByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaAnalogicaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaAnalogicaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaBit := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaBit;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_DireccionSalidaByte := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_DireccionSalidaByte;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMaxIngenieria := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMaxIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_EscaladoMinIngenieria := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_EscaladoMinIngenieria;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_Habilitar := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_Habilitar;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarAlarmaEscrituraSalida := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarAlarmaEscrituraSalida;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirConfMarcha := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarInvertirTermico := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarInvertirTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarMantenimiento := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarMantenimiento;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoConfMarcha := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoConfMarcha;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_HabilitarRetornoTermico := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_HabilitarRetornoTermico;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Config_TipoAcceso := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Config_TipoAcceso;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointAlarma := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointAlarma;
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF.Tiempos_SetPointMantenimiento := "DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF.Tiempos_SetPointMantenimiento;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la M_VF[0] que no se usa para escribir el index.
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].M_VF := "DB2016_M_VF".M_VF[0];
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldM_VF := "DB2016_M_VF".M_VF[0];
// Actualizacion de estado anterior
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].Index :=
"DB2816_M_VF_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION MULTIPLEXADO
END_FUNCTION
@@ -0,0 +1,417 @@
{
S7_Author := "ABH";
S7_BlockComment := "MLC_Xz";
S7_BlockNumber := "2817";
S7_BlockTitle := "MLC_3kB";
S7_EditorMode := "SCL";
S7_Family := "ZeusControl";
S7_Optimized := "TRUE";
S7_Version := "1.0"
}
FUNCTION "FC2817_ZC_M_SINA_MUX" : Void
VAR_INPUT
{ S7_MLC := "MLC_4wf" }
Arranque : Bool;
{ S7_MLC := "MLC_bG" }
SeguridadesOk : Bool;
END_VAR
VAR_TEMP
for_mux : Int;
t_Index : Int;
t_oldIndex : Int;
t_N_MAX : Int;
t_CambioDesdeHMI : Bool;
t_CambioDesdeMux : Bool;
END_VAR
{ S7_Language := "SCL" }
NETWORK
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
El bloque implementa la gestión de multiplexado estático para el dispositivo motor sinamics, exponiendo la matriz de equipos de la planta a los sistemas de supervisión SCADA Clásicos (WinCC Professional) sin necesidad de acceso indexado nativo.
Funcionalidades principales de la arquitectura:
- En el primer arranque del PLC (`#Arranque`), inicializa a `0` los índices (`Index` y `oldIndex`) de todos los canales de multiplexado.
- Validación de los límites del índice de cada multiplexado frente a la constante `N_MAX_DISP_M_SINA`.
- Detección de cambio de índice (`Index <> oldIndex`) para cargar la estructura completa del equipo seleccionado (`DB2017_M_SINA.M_SINA[X]`) en la interfaz del multiplexor.
- Transferencia continua de datos de Solo Lectura (estados, valores actuales, alarmas) desde el PLC hacia el SCADA.
- Transferencia bidireccional segura de datos de Escritura (consignas, configuraciones y modos) evaluando máscaras de cambios (`CambioDesdeHMI` vs `CambioDesdeMux`) para evitar sobrescrituras accidentales.
- Gestión de comandos (Resets, pulsadores) con auto-reseteo para evitar el efecto rebote.
- Caso especial de índice `0`: carga la estructura `M_SINA[0]` (reservada y vacía) para limpiar la pantalla del SCADA.
> ** Restricciones: ** Exclusivo para SCADAs sin soporte de indexación directa.
- - -
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB2017_M_SINA` |
| UDT | `ZC_DISP_M_SINA`, `ZC_DISP_MUX_M_SINA` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.07.2026 | (ABH) | Primera versión extraído a FC independiente. Refactorizacion de multiplexado para su uso solo en WinCC Professional.|
*)
END_REGION DESCRIPCION
REGION COMPROBACION_N_MAX
// Si no hay Scada declarados, salimos del bloque inmediatamente
IF "N_MAX_SCADA" <= 0 THEN
RETURN;
END_IF;
// Traspaso de N_MAX a variable temporal para evitar fallos de compilacion
#t_N_MAX := "N_MAX_SCADA";
END_REGION COMPROBACION_N_MAX
REGION MULTIPLEXADO
// ===========================================================================================================
// Al arranque del PLC, ponemos a 0 todos los indices de los multiplexados
IF #Arranque THEN
FOR #for_mux := 1 TO #t_N_MAX DO
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].Index :=
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_FOR;
END_IF;
// ===========================================================================================================
// Gestion de multiplexado
FOR #for_mux := 1 TO #t_N_MAX DO
// ===============================================================================================================
// Antes de iniciar la logica del multiplexado, revisamos los limites
REGION LIMITES
// ===========================================================================================================
// Limites
IF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].Index < 0 THEN
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].Index := 0;
END_IF;
IF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].Index > "N_MAX_DISP_M_SINA" THEN
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].Index := "N_MAX_DISP_M_SINA";
END_IF;
END_REGION
REGION INDICES
// Pasamos el indice a variable temporal para evisar cambios no deseados
#t_Index := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].Index;
#t_oldIndex := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldIndex;
END_REGION INDICES
IF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].Habilitar THEN
// ===========================================================================================================
// Gestionamos solo los tipos
IF #t_Index > 0 THEN
REGION CARGA_INICIAL
// ===========================================================================================================
// Al detectar un cambio en el indice del dispositivo, cargamos los valores del dispositivo que tenga el indice.
IF #t_oldIndex <> #t_Index THEN
// Traspaso de dispositivos al multiplexado
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA := "DB2017_M_SINA".M_SINA[#t_Index];
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA := "DB2017_M_SINA".M_SINA[#t_Index];
// ===========================================================================================================
// Valor anterior del indice
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldIndex := #t_Index;
END_IF;
END_REGION
REGION DATOS_LECTURA
// ===========================================================================================================
// Movimiento de datos gestionados en FC
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Hmi_Estado := "DB2017_M_SINA".M_SINA[#t_Index].Hmi_Estado;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_SeguridadOk := "DB2017_M_SINA".M_SINA[#t_Index].Estado_SeguridadOk;
IF NOT "DB2017_M_SINA".M_SINA[#t_Index].Estado_AutoMan AND #SeguridadesOk AND "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_SeguridadOk AND NOT "DB2017_M_SINA".M_SINA[#t_Index].Alarmas_Termico THEN
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaManual;
ELSE
IF NOT #SeguridadesOk OR NOT "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_SeguridadOk OR "DB2017_M_SINA".M_SINA[#t_Index].Alarmas_Termico THEN
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual :=
"DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual := FALSE;
END_IF;
END_IF;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_Activado := "DB2017_M_SINA".M_SINA[#t_Index].Estado_Activado;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_Enclavado := "DB2017_M_SINA".M_SINA[#t_Index].Estado_Enclavado;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_PermisoInvertirSentido := "DB2017_M_SINA".M_SINA[#t_Index].Estado_PermisoInvertirSentido;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_RetornoTermico := "DB2017_M_SINA".M_SINA[#t_Index].Estado_RetornoTermico;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_EjeBloqueado := "DB2017_M_SINA".M_SINA[#t_Index].Estado_EjeBloqueado;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_EjeHabilitado := "DB2017_M_SINA".M_SINA[#t_Index].Estado_EjeHabilitado;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_ValorActual := "DB2017_M_SINA".M_SINA[#t_Index].Estado_ValorActual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_VelocidadSalida := "DB2017_M_SINA".M_SINA[#t_Index].Estado_VelocidadSalida;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_Estado := "DB2017_M_SINA".M_SINA[#t_Index].Estado_Estado;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_CodigoFallo := "DB2017_M_SINA".M_SINA[#t_Index].Estado_CodigoFallo;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Alarmas_General := "DB2017_M_SINA".M_SINA[#t_Index].Alarmas_General;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Alarmas_Mantenimiento := "DB2017_M_SINA".M_SINA[#t_Index].Alarmas_Mantenimiento;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Alarmas_SinaSpeed := "DB2017_M_SINA".M_SINA[#t_Index].Alarmas_SinaSpeed;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Alarmas_Termico := "DB2017_M_SINA".M_SINA[#t_Index].Alarmas_Termico;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarAuto := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarAuto;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaAuto := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaAuto;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_Alarma := "DB2017_M_SINA".M_SINA[#t_Index].Tiempos_Alarma;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_Mantenimiento := "DB2017_M_SINA".M_SINA[#t_Index].Tiempos_Mantenimiento;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Aux_oldActivado := "DB2017_M_SINA".M_SINA[#t_Index].Aux_oldActivado;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Aux_oldAlarma := "DB2017_M_SINA".M_SINA[#t_Index].Aux_oldAlarma;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Aux_oldAutoMan := "DB2017_M_SINA".M_SINA[#t_Index].Aux_oldAutoMan;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_GrupoAlarma := "DB2017_M_SINA".M_SINA[#t_Index].Config_GrupoAlarma;
END_REGION
REGION DATOS_ESCRITURA
REGION GESTION_CAMBIOS_DINAMICOS
// ===========================================================================================================
// Estado Auto/Manual
IF "DB2017_M_SINA".M_SINA[#t_Index].Estado_AutoMan <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Estado_AutoMan THEN
// Cambio desde HMI
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_AutoMan := "DB2017_M_SINA".M_SINA[#t_Index].Estado_AutoMan;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Estado_AutoMan := "DB2017_M_SINA".M_SINA[#t_Index].Estado_AutoMan;
ELSIF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_AutoMan <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Estado_AutoMan THEN
// Cambio desde Multiplexado
"DB2017_M_SINA".M_SINA[#t_Index].Estado_AutoMan := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_AutoMan;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Estado_AutoMan := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_AutoMan;
END_IF;
// ===========================================================================================================
// Orden Activar Manual
IF "DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ActivarManual THEN
// Cambio desde HMI
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ActivarManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual;
ELSIF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ActivarManual THEN
// Cambio desde Multiplexado
"DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ActivarManual := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual;
END_IF;
// ===========================================================================================================
// Orden Consigna Manual
IF "DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaManual <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ConsignaManual THEN
// Cambio desde HMI
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ConsignaManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaManual;
ELSIF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaManual <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ConsignaManual THEN
// Cambio desde Multiplexado
"DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaManual := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ConsignaManual := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaManual;
END_IF;
// ===========================================================================================================
// Orden Reset horas
IF "DB2017_M_SINA".M_SINA[#t_Index].Orden_ResetHoras <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ResetHoras THEN
// Cambio desde HMI
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetHoras := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ResetHoras;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ResetHoras := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ResetHoras;
ELSIF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetHoras <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ResetHoras THEN
// Cambio desde Multiplexado
"DB2017_M_SINA".M_SINA[#t_Index].Orden_ResetHoras := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetHoras;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ResetHoras := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetHoras;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetHoras := FALSE;
END_IF;
// ===========================================================================================================
// Orden Reset fallos
IF "DB2017_M_SINA".M_SINA[#t_Index].Orden_ResetFallos <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ResetFallos THEN
// Cambio desde HMI
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetFallos := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ResetFallos;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ResetFallos := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ResetFallos;
ELSIF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetFallos <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ResetFallos THEN
// Cambio desde Multiplexado
"DB2017_M_SINA".M_SINA[#t_Index].Orden_ResetFallos := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetFallos;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ResetFallos := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ResetFallos;
END_IF;
// ===========================================================================================================
// Orden Invertir
IF "DB2017_M_SINA".M_SINA[#t_Index].Orden_SeleccionInverso <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_SeleccionInverso THEN
// Cambio desde HMI
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_SeleccionInverso := "DB2017_M_SINA".M_SINA[#t_Index].Orden_SeleccionInverso;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_SeleccionInverso := "DB2017_M_SINA".M_SINA[#t_Index].Orden_SeleccionInverso;
ELSIF "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_SeleccionInverso <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_SeleccionInverso THEN
// Cambio desde Multiplexado
"DB2017_M_SINA".M_SINA[#t_Index].Orden_SeleccionInverso := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_SeleccionInverso;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_SeleccionInverso := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_SeleccionInverso;
END_IF;
END_REGION GESTION_CAMBIOS_DINAMICOS
REGION GESTION_CAMBIOS_CONFIG
// Marca auxiliar para indicar que el cambio es desde HMI
// Comparamos el dispositivo original con el dispositivo real, para detectar cambios desde PLC o desde HMI
#t_CambioDesdeHMI :=
("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_DireccionRetTermicoBit <> "DB2017_M_SINA".M_SINA[#t_Index].Config_DireccionRetTermicoBit)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_DireccionRetTermicoByte <> "DB2017_M_SINA".M_SINA[#t_Index].Config_DireccionRetTermicoByte)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_Habilitar <> "DB2017_M_SINA".M_SINA[#t_Index].Config_Habilitar)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarInvertirTermico <> "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarInvertirTermico)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarMantenimiento <> "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarMantenimiento)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarRetornoTermico <> "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarRetornoTermico)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_K_Velocidad <> "DB2017_M_SINA".M_SINA[#t_Index].Config_K_Velocidad)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_VelocidadMax <> "DB2017_M_SINA".M_SINA[#t_Index].Config_VelocidadMax)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_VelocidadMin <> "DB2017_M_SINA".M_SINA[#t_Index].Config_VelocidadMin)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Tiempos_SetPointTiempoAlarma <> "DB2017_M_SINA".M_SINA[#t_Index].Tiempos_SetPointTiempoAlarma)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Tiempos_SetPointMantenimiento <> "DB2017_M_SINA".M_SINA[#t_Index].Tiempos_SetPointMantenimiento);
// Marca auxiliar para indicar que el cambio es desde MUX SCADA
// Comparamos el dispositivo original con el multiplexado, para detectar cambios desde multiplexado
#t_CambioDesdeMux :=
("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_DireccionRetTermicoBit <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_DireccionRetTermicoBit)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_DireccionRetTermicoByte <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_DireccionRetTermicoByte)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_Habilitar <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_Habilitar)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarInvertirTermico <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarInvertirTermico)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarMantenimiento <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarMantenimiento)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarRetornoTermico <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarRetornoTermico)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_K_Velocidad <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_K_Velocidad)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_VelocidadMax <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_VelocidadMax)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_VelocidadMin <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_VelocidadMin)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_SetPointTiempoAlarma <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Tiempos_SetPointTiempoAlarma)
OR ("DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_SetPointMantenimiento <> "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Tiempos_SetPointMantenimiento);
END_REGION GESTION_CAMBIOS_CONFIG
REGION TRASPASO_DATOS
IF #t_CambioDesdeHMI THEN
// Al detectar un cambio desde el HMI, cargamos sus valores de nuevo al multiplexado
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_AutoMan := "DB2017_M_SINA".M_SINA[#t_Index].Estado_AutoMan;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_SeleccionInverso := "DB2017_M_SINA".M_SINA[#t_Index].Orden_SeleccionInverso;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_DireccionRetTermicoBit := "DB2017_M_SINA".M_SINA[#t_Index].Config_DireccionRetTermicoBit;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_DireccionRetTermicoByte := "DB2017_M_SINA".M_SINA[#t_Index].Config_DireccionRetTermicoByte;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_Habilitar := "DB2017_M_SINA".M_SINA[#t_Index].Config_Habilitar;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarInvertirTermico := "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarInvertirTermico;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarMantenimiento := "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarMantenimiento;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarRetornoTermico := "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarRetornoTermico;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_K_Velocidad := "DB2017_M_SINA".M_SINA[#t_Index].Config_K_Velocidad;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_VelocidadMax := "DB2017_M_SINA".M_SINA[#t_Index].Config_VelocidadMax;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_VelocidadMin := "DB2017_M_SINA".M_SINA[#t_Index].Config_VelocidadMin;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_SetPointTiempoAlarma := "DB2017_M_SINA".M_SINA[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_SetPointMantenimiento := "DB2017_M_SINA".M_SINA[#t_Index].Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Estado_AutoMan := "DB2017_M_SINA".M_SINA[#t_Index].Estado_AutoMan;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ActivarManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ConsignaManual := "DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_SeleccionInverso := "DB2017_M_SINA".M_SINA[#t_Index].Orden_SeleccionInverso;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_DireccionRetTermicoBit := "DB2017_M_SINA".M_SINA[#t_Index].Config_DireccionRetTermicoBit;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_DireccionRetTermicoByte := "DB2017_M_SINA".M_SINA[#t_Index].Config_DireccionRetTermicoByte;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_Habilitar := "DB2017_M_SINA".M_SINA[#t_Index].Config_Habilitar;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarInvertirTermico := "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarInvertirTermico;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarMantenimiento := "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarMantenimiento;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarRetornoTermico := "DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarRetornoTermico;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_K_Velocidad := "DB2017_M_SINA".M_SINA[#t_Index].Config_K_Velocidad;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_VelocidadMax := "DB2017_M_SINA".M_SINA[#t_Index].Config_VelocidadMax;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_VelocidadMin := "DB2017_M_SINA".M_SINA[#t_Index].Config_VelocidadMin;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Tiempos_SetPointTiempoAlarma := "DB2017_M_SINA".M_SINA[#t_Index].Tiempos_SetPointTiempoAlarma;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Tiempos_SetPointMantenimiento := "DB2017_M_SINA".M_SINA[#t_Index].Tiempos_SetPointMantenimiento;
ELSIF #t_CambioDesdeMux THEN
// Al detectar un cambio desde el MUX, cargamos sus valores al dispositivo
"DB2017_M_SINA".M_SINA[#t_Index].Estado_AutoMan := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_AutoMan;
"DB2017_M_SINA".M_SINA[#t_Index].Orden_ActivarManual := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual;
"DB2017_M_SINA".M_SINA[#t_Index].Orden_ConsignaManual := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaManual;
"DB2017_M_SINA".M_SINA[#t_Index].Orden_SeleccionInverso := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_SeleccionInverso;
"DB2017_M_SINA".M_SINA[#t_Index].Config_DireccionRetTermicoBit := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_DireccionRetTermicoBit;
"DB2017_M_SINA".M_SINA[#t_Index].Config_DireccionRetTermicoByte := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_DireccionRetTermicoByte;
"DB2017_M_SINA".M_SINA[#t_Index].Config_Habilitar := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_Habilitar;
"DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarInvertirTermico := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarInvertirTermico;
"DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarMantenimiento := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarMantenimiento;
"DB2017_M_SINA".M_SINA[#t_Index].Config_HabilitarRetornoTermico := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarRetornoTermico;
"DB2017_M_SINA".M_SINA[#t_Index].Config_K_Velocidad := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_K_Velocidad;
"DB2017_M_SINA".M_SINA[#t_Index].Config_VelocidadMax := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_VelocidadMax;
"DB2017_M_SINA".M_SINA[#t_Index].Config_VelocidadMin := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_VelocidadMin;
"DB2017_M_SINA".M_SINA[#t_Index].Tiempos_SetPointTiempoAlarma := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_SetPointTiempoAlarma;
"DB2017_M_SINA".M_SINA[#t_Index].Tiempos_SetPointMantenimiento := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_SetPointMantenimiento;
// Actualizamos el estado original para configuraciones
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Estado_AutoMan := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Estado_AutoMan;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ActivarManual := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ActivarManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_ConsignaManual := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_ConsignaManual;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Orden_SeleccionInverso := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Orden_SeleccionInverso;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_DireccionRetTermicoBit := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_DireccionRetTermicoBit;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_DireccionRetTermicoByte := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_DireccionRetTermicoByte;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_Habilitar := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_Habilitar;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarInvertirTermico := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarInvertirTermico;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarMantenimiento := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarMantenimiento;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_HabilitarRetornoTermico := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_HabilitarRetornoTermico;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_K_Velocidad := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_K_Velocidad;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_VelocidadMax := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_VelocidadMax;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Config_VelocidadMin := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Config_VelocidadMin;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Tiempos_SetPointTiempoAlarma := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_SetPointTiempoAlarma;
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA.Tiempos_SetPointMantenimiento := "DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA.Tiempos_SetPointMantenimiento;
END_IF;
END_REGION TRASPASO_DATOS
END_REGION DATOS_ESCRITURA
ELSE
// ===========================================================================================================
// En caso de que el indice sea 0, movemos el valor de la M_SINA[0] que no se usa para escribir el index.
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].M_SINA := "DB2017_M_SINA".M_SINA[0];
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldM_SINA := "DB2017_M_SINA".M_SINA[0];
// Actualizacion de estado anterior
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
ELSE
// Multiplexado no habilitado
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].Index :=
"DB2817_M_SINA_MUX".Mux.Scada[#for_mux].oldIndex := 0;
END_IF;
END_FOR;
END_REGION
END_NETWORK
END_FUNCTION

Some files were not shown because too many files have changed in this diff Show More