Reorganizacion de libreria.

This commit is contained in:
2026-07-20 08:15:06 +02:00
parent e0b9f4a206
commit 2b9dbc1f27
280 changed files with 9799 additions and 9398 deletions
@@ -0,0 +1,135 @@
FUNCTION "FC17_ZC_GEST_ALM" : Void
TITLE = FC17_ZC_GEST_ALM
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
VAR_INPUT
PrimerCiclo : Bool; // Marca de primer ciclo de arranque de la CPU
FechaHora {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
Usuario : String; // Usuario actual
PInt : Array[*] of "UDT_ZC_PINT"; // Array de parametro entero
IndexTraza : Int; // Indice de texto parametro para trazabilidad
END_VAR
VAR_IN_OUT
ValorAnterior : Array[*] of Int; // Array de valores anteriores
END_VAR
VAR_TEMP
for_i : DInt; // Variable para bucles FOR
t_IndiceSuperior : DInt; // Indice superior del array
t_IndiceInferior : DInt; // Indice inferior del array
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:** DB Optimizado.
---
### Descripción Funcional
Función principal para la gestión de trazabilidad de los parámetros **enteros** del proceso.
Su objetivo principal es detectar cambios de valor en el array de parámetros y lanzar un registro de traza cuando esto sucede.
El bloque opera de la siguiente manera:
1. Calcula dinámicamente los límites superior e inferior del array de entrada.
2. Si es el primer ciclo de ejecución de la CPU, inicializa los valores "anteriores" con los valores "actuales" para evitar falsos registros de traza iniciales.
3. En ciclos normales, compara el valor actual de cada parámetro con su valor guardado en el ciclo anterior.
4. Si detecta una diferencia, llama a la función `FC8_ZC_TRAZA_REGISTRO` para registrar el cambio y actualiza el valor anterior.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_PINT` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 2026-03-20 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION CALCULO_LIMITES_ARRAY
// Calculo de los limites del array, ya que la entrada del array de parametros es variable.
#t_IndiceInferior := LOWER_BOUND(ARR := #PInt, DIM := 1);
#t_IndiceSuperior := UPPER_BOUND(ARR := #PInt, DIM := 1);
END_REGION CALCULO_LIMITES_ARRAY
// ==========================================================================================================
REGION PRIMER_ARRANQUE
// En el primer arranque de la CPU, recorremos el array completo y almacenamos los valores actuales como valores anteriores en el arranque de la CPU.
// Esto se realiza ya que los valores anteriores no son remanentes, para optimizar la memoria del PLC.
IF #PrimerCiclo THEN
FOR #for_i := #t_IndiceInferior TO #t_IndiceSuperior DO
#ValorAnterior[#for_i] := #PInt[#for_i].Valor;
END_FOR;
// Salimos inmediatamente de la funcion, para realizar la gestion de los parametros con la CPU arrancada
RETURN;
END_IF;
END_REGION PRIMER_ARRANQUE
// ==========================================================================================================
REGION GESTION_PARAMETROS
// Gestion de los cambios de valor de los parametros. Si detecta un cambio, lanza la traza
// y actualiza el valor anterior.
FOR #for_i := #t_IndiceInferior TO #t_IndiceSuperior DO
// En caso de detectar cambio de valor
IF #PInt[#for_i].Valor <> #ValorAnterior[#for_i] THEN
// Lanzamos la trazabilidad
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHora,
Registrar := 1,
Categoria := "TRZ_CAT_26_PINT",
User := #Usuario,
CodInt_1 := #IndexTraza + DINT_TO_INT(#for_i - 1),
CodInt_2 := "TRZ_SYS_PAR_VAL_ANT",
CodInt_3 := "TRZ_SYS_PAR_VAL_NUE",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := #ValorAnterior[#for_i],
CodReal_2 := #PInt[#for_i].Valor);
// Actualizamos el valor anterior
#ValorAnterior[#for_i] := #PInt[#for_i].Valor;
END_IF;
END_FOR;
END_REGION GESTION_PARAMETROS
END_FUNCTION
@@ -0,0 +1,24 @@
DATA_BLOCK "DB11_INF"
TITLE = DB11_INFORMES
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
NON_RETAIN
VAR RETAIN
Gestion { S7_SetPoint := 'False'} : "UDT_ZC_INF_GESTION";
Buffer { S7_SetPoint := 'False'} : Array[0.."N_MAX_INF_BUFFER"] of "UDT_ZC_INF_DATOS";
END_VAR
BEGIN
Gestion.HabilitacionGeneral := TRUE;
Gestion.EstadoConexion := TRUE;
Gestion.OrdenIniciar := FALSE;
Gestion.Etapa := 0;
Gestion.SP_Tiempo := 10;
Gestion.TiempoActual := 0;
Gestion.NumeroMaxIntentos := 3;
END_DATA_BLOCK
@@ -0,0 +1,246 @@
FUNCTION_BLOCK "FB15051_ZC_INF_CPR"
TITLE = FB15051_ZC_INF_CPR
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
VAR_INPUT
GuardarDatos : Bool; // Orden guardar datos
FinGuardarDatos : Bool; // Orden finalizar guardar datos
FechaActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
TipoProceso : Int; // Tipo de proceso
Destino : Int; // Destino
Producto : Int; // Producto
Producto1Destino : Int; // Producto 1 en destino
Producto2Destino : Int; // Producto 2 en destino
Nombre : String[40]; // Nombre
Matricula : String[10]; // Matricula
LetraQ : String[10]; // Letra Q
KilosSP : Real; // Kilos SP
KilosDescargados : Real; // Cantidad
DensidadMedia : LReal; // Densidad Media
TempMediaDescarga : LReal; // Temperatura media en descarga
TempMediaEnfriamiento : LReal; // Temperatura media enfriamiento
EmpujeRealizado : Bool; // Empuje realizado
AclaradoRealizado : Bool; // Aclarado realizado
END_VAR
VAR_OUTPUT
GuardarSQL : Bool;
END_VAR
VAR_IN_OUT
Mux : "UDT_ZC_MUX"; // Variable Mux
Informe : Array[*] of "UDT_ZC_INF_CPR_PROD"; // Informes de compacto
END_VAR
VAR RETAIN
s_OrdenGuardarDatos : Bool; // Orden adquirir datos de informes
s_OrdenGuardarFechaInicio : Bool; // Orden guardar fecha de inicio y dezplazar informes
END_VAR
VAR
s_OrdenGuardarFechaFin : Bool; // Orden guardar fecha de fin
s_OrdenBorrarInforme : Bool; // Orden borrar informe
s_NumeroBorrado : Int; // -1= Todos / Valor= Numero de registro
END_VAR
VAR RETAIN
s_P_Trig_TomarFechaInicio : Bool; // Pulso
s_P_Trig_TomarFechaFin : Bool; // Pulso
END_VAR
VAR_TEMP
for_i : Int; // Variable para bucles FOR
t_dummyInt : Int;
t_IndiceSuperior : Int; // Indice superior del array
t_IndiceInferior : Int; // Indice inferior del array
t_InformeVacio : "UDT_ZC_INF_CPR_PROD";
END_VAR
VAR CONSTANT
ACTUAL : Int := 1;
TIPO_DESCARGA_PARCIAL : Int := 1; // TIPO DE PROCESO DESCARGA PARCIAL
TIPO_DESCARGA_TOTAL : Int := 2; // TIPO DE PROCESO DESCARGA TOTAL
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2023 - 2026
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Bloque de funcion para la gestión de informes de descargas de compacto de recepcion.
En ella se realizan las siguientes acciones:
- Cálculo dinámico de indices de array de informes.
- Gestion de ordenes de inicio y fin informe.
- Desplazamiento y captura de datos de informe.
- Borrado de informes.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15005_ZC_DTL_TO_ZC_FECHA` |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_MUX`, `UDT_ZC_INF_CPR_PROD` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 01.08.2023 | (HCR) | Primera version. |
| 00.00.02 | 16.04.2024 | (ABH) | Se cambian los nombre a variables para estandarizar. |
| 01.00.02 | 13.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION LIMITES_Y_MULTIPLEXADO
// Calculo limites array de informes
#t_IndiceInferior := DINT_TO_INT(LOWER_BOUND(ARR := #Informe, DIM := 1));
#t_IndiceSuperior := DINT_TO_INT(UPPER_BOUND(ARR := #Informe, DIM := 1));
// Si no hay informes configurados, salimos inmediatamente.
IF #t_IndiceInferior <= 0 THEN
#Mux.Index := 0;
RETURN;
END_IF;
// Limites multiplexado
IF #Mux.Index < 1 THEN
#Mux.Index := 0;
END_IF;
IF #Mux.Index > #t_IndiceSuperior THEN
#Mux.Index := #t_IndiceSuperior;
END_IF;
END_REGION LIMITES_Y_MULTIPLEXADO
// ==========================================================================================================
REGION ORDEN_INICIO_FIN
// Orden de inicio
IF #GuardarDatos AND NOT #s_P_Trig_TomarFechaInicio THEN
#s_OrdenGuardarFechaInicio := TRUE;
#s_OrdenGuardarDatos := TRUE;
END_IF;
// Orden de fin
#GuardarSQL := FALSE;
IF #FinGuardarDatos AND NOT #s_P_Trig_TomarFechaFin THEN
#s_OrdenGuardarFechaFin := TRUE;
#GuardarSQL := TRUE;
END_IF;
// Estados anteriores
#s_P_Trig_TomarFechaInicio := #GuardarDatos;
#s_P_Trig_TomarFechaFin := #FinGuardarDatos;
END_REGION ORDEN_INICIO_FIN
// ==========================================================================================================
REGION ADQUISICION_DE_DATOS
// Con flanco de guardar datos fecha de inicio, desplazamos los registros y almacenamos la fecha de inicio
IF #s_OrdenGuardarFechaInicio THEN
// Desplazamiento de datos
#t_dummyInt := MOVE_BLK_VARIANT(SRC := #Informe, COUNT := INT_TO_UDINT(#t_IndiceSuperior - 1), SRC_INDEX := 0, DEST_INDEX := 1, DEST => #Informe);
// Tomar valor de fecha de inicio
#Informe[#ACTUAL].FechaInicio := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaActual);
#s_OrdenGuardarFechaInicio := FALSE;
END_IF;
// Tomar datos
IF #s_OrdenGuardarDatos THEN
#Informe[#ACTUAL].Descarga.TipoProceso := #TipoProceso;
#Informe[#ACTUAL].Descarga.Destino := #Destino;
IF #TipoProceso = #TIPO_DESCARGA_PARCIAL OR #TipoProceso = #TIPO_DESCARGA_TOTAL THEN
#Informe[#ACTUAL].Descarga.Producto := #Producto;
#Informe[#ACTUAL].Descarga.Producto1Destino := #Producto1Destino;
#Informe[#ACTUAL].Descarga.Producto2Destino := #Producto2Destino;
#Informe[#ACTUAL].Descarga.KilosSP := #KilosSP;
#Informe[#ACTUAL].Descarga.KilosDescargados := #KilosDescargados;
#Informe[#ACTUAL].Descarga.DensidadMedia := #DensidadMedia;
#Informe[#ACTUAL].Descarga.TemperaturaMediaDescarga := #TempMediaDescarga;
#Informe[#ACTUAL].Descarga.TemperaturaMediaEnfriamiento := #TempMediaEnfriamiento;
#Informe[#ACTUAL].Descarga.Nombre := #Nombre;
#Informe[#ACTUAL].Descarga.Matricula := #Matricula;
#Informe[#ACTUAL].Descarga.LetraQ := #LetraQ;
END_IF;
#Informe[#ACTUAL].Descarga.EmpujeRealizado := #EmpujeRealizado;
#Informe[#ACTUAL].Descarga.AclaradoRealizado := #AclaradoRealizado;
END_IF;
// Con flanco de guardar datos fecha de fin almacenamos la fecha de fin y resetemos guardar datos
IF #s_OrdenGuardarFechaFin THEN
#Informe[#ACTUAL].FechaFin := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaActual);
#s_OrdenGuardarDatos := FALSE;
#s_OrdenGuardarFechaFin := FALSE;
END_IF;
END_REGION ADQUISICION_DE_DATOS
// ==========================================================================================================
REGION BORRADO_INFORMES
// Borrado de todos los registros
IF #s_OrdenBorrarInforme THEN
IF #s_NumeroBorrado = -1 OR #s_NumeroBorrado = 0 THEN
// Borrado total
FOR #for_i := 1 TO #t_IndiceSuperior DO
#Informe[#for_i] := #t_InformeVacio;
END_FOR;
ELSIF #s_NumeroBorrado >= 1 AND #s_NumeroBorrado < #t_IndiceSuperior THEN
// Borrado registro especifico
#Informe[#s_NumeroBorrado] := #t_InformeVacio;
// Desplazamiento de datos
#t_dummyInt := MOVE_BLK_VARIANT(SRC := #Informe,
COUNT := INT_TO_UDINT(#t_IndiceSuperior - #s_NumeroBorrado),
SRC_INDEX := #s_NumeroBorrado,
DEST_INDEX := #s_NumeroBorrado - 1,
DEST => #Informe);
// Borrado del ultimo registro
#Informe[#t_IndiceSuperior] := #t_InformeVacio;
ELSIF #s_NumeroBorrado = #t_IndiceSuperior THEN
// Borrado de ultimo registro
#Informe[#s_NumeroBorrado] := #t_InformeVacio;
END_IF;
#s_OrdenBorrarInforme := FALSE;
END_IF;
END_REGION BORRADO_INFORMES
END_FUNCTION_BLOCK
@@ -0,0 +1,236 @@
FUNCTION_BLOCK "FB15052_ZC_INF_PROD_MEM"
TITLE = FB15052_ZC_INF_PROD_MEM
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Bloque de gestion para adquisicion de datos para informe de produccion maquina membranas
VAR_INPUT
OrdenInicioInforme { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Orden inicio informe
OrdenFinInforme : Bool; // Orden fin informe
EnProduccion : Bool; // En produccion
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DTL; // Fecha y hora actual
Producto : Int; // Producto
DestinoConcentrado : Int; // Destino concentrado
DestinoPermeado : Int; // Destino permeado
LitrosEntrada : LReal; // Totalizador litros Entrada
LitrosConcentrado : LReal; // Totalizador litros Concentrado
Brix : LReal; // Media Brix en produccion
Fcv : Real; // Media FCV En produccion
TempEntrada : LReal; // Media temperatura entrada
TempConcentrado : LReal; // Media temperatura concentrado
TempPermeado : LReal; // Media temperatura permeado
TiempoProduccion : "UDT_ZC_CONTADOR_TIEMPO"; // Tiempo Produccion
END_VAR
VAR_IN_OUT
Informe : Array[*] of "UDT_ZC_INF_PROD_MEM";
END_VAR
VAR
s_OrdenInicioInforme { S7_SetPoint := 'True'} : Bool; // Orden inicio informe
s_OrdenFinInforme { S7_SetPoint := 'True'} : Bool; // Orden fin informe
END_VAR
VAR RETAIN
s_oldInicio { S7_SetPoint := 'True'} : Bool; // Estado anterior inicio
s_oldFin { S7_SetPoint := 'True'} : Bool; // Estado anterior fin
END_VAR
VAR
s_OrdenBorrarInforme : Bool; // Orden borrar informe
s_NumeroBorrado : Int; // -1= Todos / Valor= Numero de registro
END_VAR
VAR_TEMP
for_i : Int;
t_dummyInt : Int;
t_IndiceSuperior : Int; // Indice superior del array
t_IndiceInferior : Int; // Indice inferior del array
t_InformeVacio : "UDT_ZC_INF_PROD_MEM";
END_VAR
VAR CONSTANT
ACTUAL : Int := 1;
END_VAR
BEGIN
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2023 - 2026
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
Bloque de funcion que implementa la gestión de los datos de informe de producción de una máquina de membranas, manteniendo un histórico de hasta `#N_REGISTROS` producciones con todos los datos relevantes del proceso (volúmenes, Brix, FCV, temperaturas, tiempos, destinos y producto) para su trazabilidad y consulta desde el sistema de supervisión.
Funcionalidades principales:
- Detección de flanco ascendente en las órdenes `#OrdenInicioInforme` y `#OrdenFinInforme` mediante las variables estáticas `#s_P_Trig_Inicio` y `#s_P_Trig_Fin`, generando los pulsos internos `#s_OrdenInicioInforme` y `#s_OrdenFinInforme`.
- Inicialización de informe: al recibir `#s_OrdenInicioInforme` se desplazan los informes existentes una posición hacia abajo mediante `MOVE_BLK_VARIANT`, se reinicializan los campos del nuevo informe activo (`#Informe[#ACTUAL]`: fechas, tiempo de producción, destinos, producto, litros, Brix, FCV y temperaturas) y se almacena la fecha/hora de inicio mediante la conversión realizada por `FC15005_ZC_DTL_TO_ZC_FECHA`.
- Datos en curso: mientras `#EnProduccion` está activo, se copian en `#Informe[#ACTUAL]` los valores de proceso en tiempo real: tiempo de producción, destinos de concentrado y permeado, código de producto, litros (entrada, concentrado y permeado por diferencia), Brix, FCV y las tres temperaturas (concentrado, permeado y entrada), aplicando `LREAL_TO_REAL` para adaptar la precisión al tipo de dato del UDT.
- Fin de informe: al detectar `#s_OrdenFinInforme` se registra la fecha/hora de fin en `#Informe[#ACTUAL].FechaFin`.
- Borrado masivo: la entrada `#s_Borrar` permite limpiar completamente todos los registros del array, inicializando fechas, tiempos, litros, Brix, FCV, temperatura de concentrado y código de producto a sus valores neutros. La propia variable de orden se autodesactiva tras la operación.
> **Restricciones:** PLC serie 1200/1500.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15005_ZC_DTL_TO_ZC_FECHA` |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_CONTADOR_TIEMPO`, `UDT_ZC_INF_PROD_MEM` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 24.05.2023 | (ABH) | Primera versión. |
| 01.00.01 | 16.02.2024 | (ABH) | Cambios en datos de REAL a LREAL para coincidir con UDT. |
| 01.00.02 | 23.05.2024 | (HCR) | Se añaden temperaturas de entrada, destinos concentrado y destinos permeado. |
| 01.00.03 | 03.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION LIMITES
// Calculo limites array de informes
#t_IndiceInferior := DINT_TO_INT(LOWER_BOUND(ARR := #Informe, DIM := 1));
#t_IndiceSuperior := DINT_TO_INT(UPPER_BOUND(ARR := #Informe, DIM := 1));
// Si no hay informes configurados, salimos inmediatamente.
IF #t_IndiceInferior <= 0 THEN
RETURN;
END_IF;
END_REGION LIMITES
// ==========================================================================================================
REGION GESTION_ORDENES
// Orden de inicio
#s_OrdenInicioInforme := #OrdenInicioInforme AND NOT #s_oldInicio;
// Orden de fin
#s_OrdenFinInforme := #OrdenFinInforme AND NOT #s_oldFin;
// Estados anteriores
#s_oldInicio := #OrdenInicioInforme;
#s_oldFin := #OrdenFinInforme;
END_REGION GESTION_ORDENES
// ==========================================================================================================
REGION INICIALIZACION
// Con flanco de inicio informe, desplazamos los registros, reinicializamos los datos y almacenamos la fecha y hora de inicio, todo en la poisicion 1
IF #s_OrdenInicioInforme THEN
// Desplazamiento de datos
#t_dummyInt := MOVE_BLK_VARIANT(SRC := #Informe, COUNT := INT_TO_UDINT(#t_IndiceSuperior - 1), SRC_INDEX := 0, DEST_INDEX := 1, DEST => #Informe);
// Reinicializacion de los datos
#Informe[#ACTUAL] := #t_InformeVacio;
// Guardado de fecha y hora actual
#Informe[#ACTUAL].FechaInicio := "FC15005_ZC_DTL_TO_ZC_FECHA"(FechaDTL := #FechaHoraActual);
END_IF;
END_REGION INICIALIZACION
// ==========================================================================================================
REGION DATOS_DURANTE_PRODUCCION
// Se guardan los datos durante la produccion
IF #EnProduccion THEN
#Informe[#ACTUAL].TiempoProduccion.Hora := #TiempoProduccion.Hora;
#Informe[#ACTUAL].TiempoProduccion.Minuto := #TiempoProduccion.Minuto;
#Informe[#ACTUAL].TiempoProduccion.Segundo := #TiempoProduccion.Segundo;
#Informe[#ACTUAL].DestinoConcentrado := #DestinoConcentrado;
#Informe[#ACTUAL].DestinoPermeado := #DestinoPermeado;
#Informe[#ACTUAL].Producto := #Producto;
#Informe[#ACTUAL].LitrosEntrada := LREAL_TO_REAL(#LitrosEntrada);
#Informe[#ACTUAL].LitrosConcentrado := LREAL_TO_REAL(#LitrosConcentrado);
#Informe[#ACTUAL].LitrosPermeado := LREAL_TO_REAL(#LitrosEntrada - #LitrosConcentrado);
#Informe[#ACTUAL].Brix := LREAL_TO_REAL(#Brix);
#Informe[#ACTUAL].FCV := #Fcv;
#Informe[#ACTUAL].TempConcentrado := LREAL_TO_REAL(#TempConcentrado);
#Informe[#ACTUAL].TempPermeado := LREAL_TO_REAL(#TempPermeado);
#Informe[#ACTUAL].TempEntrada := LREAL_TO_REAL(#TempEntrada);
END_IF;
END_REGION DATOS_DURANTE_PRODUCCION
// ==========================================================================================================
REGION FIN_INFORME
// Se guardan los datos durante la produccion
IF #s_OrdenFinInforme THEN
// Guardado de fecha y hora actual
#Informe[#ACTUAL].FechaFin := "FC15005_ZC_DTL_TO_ZC_FECHA"(FechaDTL := #FechaHoraActual);
END_IF;
END_REGION FIN_INFORME
// ==========================================================================================================
REGION BORRADO_DATOS
// Borrado de todos los registros
IF #s_OrdenBorrarInforme THEN
IF #s_NumeroBorrado = -1 OR #s_NumeroBorrado = 0 THEN
FOR #for_i := 1 TO #t_IndiceSuperior DO
#Informe[#for_i] := #t_InformeVacio;
END_FOR;
ELSIF #s_NumeroBorrado >= 1 AND #s_NumeroBorrado < #t_IndiceSuperior THEN
// Borrado registro especifico
#Informe[#s_NumeroBorrado] := #t_InformeVacio;
// Desplazamiento de datos
#t_dummyInt := MOVE_BLK_VARIANT(SRC := #Informe,
COUNT := INT_TO_UDINT(#t_IndiceSuperior - #s_NumeroBorrado),
SRC_INDEX := #s_NumeroBorrado,
DEST_INDEX := #s_NumeroBorrado - 1,
DEST => #Informe);
// Borrado del ultimo registro
#Informe[#t_IndiceSuperior] := #t_InformeVacio;
ELSIF #s_NumeroBorrado = #t_IndiceSuperior THEN
// Borrado de ultimo registro
#Informe[#s_NumeroBorrado] := #t_InformeVacio;
END_IF;
#s_OrdenBorrarInforme := FALSE;
END_IF;
END_REGION BORRADO_DATOS
END_FUNCTION_BLOCK
@@ -0,0 +1,264 @@
FUNCTION_BLOCK "FB15053_ZC_INF_LIMP_MAQ"
TITLE = FB15053_ZC_INF_LIMP_MAQ
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Bloque de gestion para adquisicion de datos para informe de limpieza de maquinas
VAR_INPUT
EnLimpieza : Bool; // En limpieza
LimpBasica1 : Bool; // En limpieza basica 1
LimpAcida : Bool; // En limpieza acida
LimpBasica2 : Bool; // En limpieza basica 2
Pulso1Seg : Bool; // Pulso de 1 segundo
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DTL; // Fecha y hora actual
TempBasica1 : LReal; // Temperatura basica 1
CTLimpBasica1 : LReal; // Conductividad limpieza basica 1
CTEnjBasica1 : LReal; // Conductividad enjuague basica 1
PorcentajeQRefBasica1 : LReal; // Porcentaje Q Referencia basica 1
TempAcida : LReal; // Temperatura acida
CTLimpAcida : LReal; // Conductividad limpieza acida
CTEnjAcida : LReal; // Conductividad enjuague acida
PorcentajeQRefAcida : LReal; // Porcentaje Q Referencia acida
TempBasica2 : LReal; // Temperatura basica 2
CTLimpBasica2 : LReal; // Conductividad limpieza basica 2
CTEnjBasica2 : LReal; // Conductividad enjuague basica 2
PorcentajeQRefBasica2 : LReal; // Porcentaje Q Referencia basica 2
TipoLimpieza : Int; // Tipo de limpieza
TiempoLimpieza : "UDT_ZC_CONTADOR_TIEMPO"; // Tiempo de limpieza
END_VAR
VAR_IN_OUT
Informe : Array[*] of "UDT_ZC_INF_LIMP_MAQ"; // Informe
END_VAR
VAR
s_OrdenInicioInforme { S7_SetPoint := 'True'} : Bool; // Orden inicio informe
s_OrdenFinInforme { S7_SetPoint := 'True'} : Bool; // Orden fin informe
END_VAR
VAR RETAIN
s_oldInicio { S7_SetPoint := 'True'} : Bool; // Estado anterior inicio
s_oldFin { S7_SetPoint := 'True'} : Bool; // Estado anterior fin
END_VAR
VAR
s_OrdenBorrarInforme : Bool; // Orden borrar informe
s_NumeroBorrado : Int; // -1= Todos / Valor= Numero de registro
END_VAR
VAR RETAIN
TiempoRecircBasica1 : Int; // Tiempo en recirculacion basica 1
TiempoRecircAcida : Int; // Tiempo en recirculacion acida
TiempoRecircBasica2 : Int; // Tiempo en recirculacion basica 2
END_VAR
VAR_TEMP
for_i : Int;
t_dummyInt : Int;
t_IndiceSuperior : Int; // Indice superior del array
t_IndiceInferior : Int; // Indice inferior del array
t_InformeVacio : "UDT_ZC_INF_LIMP_MAQ";
END_VAR
VAR CONSTANT
ACTUAL : Int := 1;
END_VAR
BEGIN
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2025 - 2026
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
Bloque de funcion que implementa la gestión de los datos de informe de limpieza de la máquina, manteniendo un histórico de hasta `#N_REGISTROS` limpiezas con todos los datos relevantes de cada fase (básica 1, ácida y básica 2): tiempo total, tipo de limpieza, temperaturas, conductividades de limpieza y enjuague, porcentaje de caudal de referencia y tiempo de recirculación por etapa. Se utiliza en máquinas de membranas para trazabilidad y consulta desde el sistema de supervisión.
Funcionalidades principales:
- Detección de flanco ascendente y descendente en `#EnLimpieza` mediante las variables estáticas `#s_P_Trig_Inicio` y `#s_P_Trig_Fin`, generando el pulso de inicio `#s_OrdenInicioInforme` y el de fin `#s_OrdenFinInforme`.
- Inicialización del informe: al recibir `#s_OrdenInicioInforme` se desplazan los informes existentes una posición hacia abajo mediante `MOVE_BLK_VARIANT`, se reinicializan a cero los contadores internos de recirculación (`#TiempoRecircBasica1`, `#TiempoRecircAcida`, `#TiempoRecircBasica2`) y los campos del nuevo informe activo (`#Informe[1]`: tipo de limpieza, tiempos de recirculación por fase, tiempo total, temperaturas, conductividades y porcentajes de caudal de referencia de cada fase). Se almacena la fecha/hora de inicio mediante la conversión realizada por `FC15005_ZC_DTL_TO_ZC_FECHA`.
- Datos en curso: mientras `#EnLimpieza` está activo, se incrementan los contadores de tiempo de recirculación de cada fase (`#TiempoRecircBasica1`, `#TiempoRecircAcida`, `#TiempoRecircBasica2`) cuando el pulso de 1 segundo y el flag de fase correspondiente (`#LimpBasica1`, `#LimpAcida`, `#LimpBasica2`) están activos, y se copian en `#Informe[1]` los valores de proceso en tiempo real: tipo de limpieza, tiempo total de limpieza, temperatura, conductividad de limpieza, conductividad de enjuague, porcentaje de caudal de referencia y tiempo de recirculación para cada una de las tres fases.
- Fin de informe: al detectar `#s_OrdenFinInforme` se registra la fecha/hora de fin en `#Informe[1].FechaFin`.
- Borrado masivo: la entrada `#s_Borrar` permite limpiar completamente todos los registros del array, inicializando fechas, tipo de limpieza, tiempos de recirculación, tiempo total, temperaturas, conductividades y porcentajes de caudal de referencia a sus valores neutros. La propia variable de orden se autodesactiva tras la operación.
> **Restricciones:** Ninguna.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15005_ZC_DTL_TO_ZC_FECHA` |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_CONTADOR_TIEMPO`, `ZC_INF_LIMP_MAQ` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.11.2025 | (CNR) | Primera versión. |
| 01.00.01 | 03.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION LIMITES
// Calculo limites array de informes
#t_IndiceInferior := DINT_TO_INT(LOWER_BOUND(ARR := #Informe, DIM := 1));
#t_IndiceSuperior := DINT_TO_INT(UPPER_BOUND(ARR := #Informe, DIM := 1));
// Si no hay informes configurados, salimos inmediatamente.
IF #t_IndiceInferior <= 0 THEN
RETURN;
END_IF;
END_REGION LIMITES
// ==========================================================================================================
REGION GESTION_ORDENES
// Orden inicio produccion
#s_OrdenInicioInforme := #EnLimpieza AND NOT #s_oldInicio;
// Orden fin produccion
#s_OrdenFinInforme := NOT #EnLimpieza AND #s_oldFin;
// Estados anteriores
#s_oldInicio := #EnLimpieza;
#s_oldFin := #EnLimpieza;
END_REGION GESTION_ORDENES
// ==========================================================================================================
REGION INICIALIZACION
// Con flanco de inicio informe, desplazamos los registros, reinicializamos los datos y almacenamos la fecha y hora de inicio, todo en la poisicion 1
IF #s_OrdenInicioInforme THEN
// Desplazamiento de datos
#t_dummyInt := MOVE_BLK_VARIANT(SRC := #Informe, COUNT := INT_TO_UDINT(#t_IndiceSuperior - 1), SRC_INDEX := 0, DEST_INDEX := 1, DEST => #Informe);
// Reinicializacion de los datos
#Informe[#ACTUAL] := #t_InformeVacio;
#TiempoRecircBasica1 := 0;
#TiempoRecircAcida := 0;
#TiempoRecircBasica2 := 0;
// Guardado de fecha y hora actual
#Informe[#ACTUAL].FechaInicio := "FC15005_ZC_DTL_TO_ZC_FECHA"(FechaDTL := #FechaHoraActual);
END_IF;
END_REGION INICIALIZACION
// ==========================================================================================================
REGION DATOS_DURANTE_PRODUCCION
// Se guardan los datos durante la produccion
IF #EnLimpieza THEN
IF #Pulso1Seg AND #LimpBasica1 THEN
#TiempoRecircBasica1 += 1;
END_IF;
IF #Pulso1Seg AND #LimpAcida THEN
#TiempoRecircAcida += 1;
END_IF;
IF #Pulso1Seg AND #LimpBasica2 THEN
#TiempoRecircBasica2 += 1;
END_IF;
#Informe[#ACTUAL].TipoLimpieza := #TipoLimpieza;
#Informe[#ACTUAL].TiempoLimpieza.Hora := #TiempoLimpieza.Hora;
#Informe[#ACTUAL].TiempoLimpieza.Minuto := #TiempoLimpieza.Minuto;
#Informe[#ACTUAL].TiempoLimpieza.Segundo := #TiempoLimpieza.Segundo;
#Informe[#ACTUAL].TempBasica1 := #TempBasica1;
#Informe[#ACTUAL].CTLimpBasica1 := #CTLimpBasica1;
#Informe[#ACTUAL].CTEnjBasica1 := #CTEnjBasica1;
#Informe[#ACTUAL].PorcentajeQRefBasica1 := #PorcentajeQRefBasica1;
#Informe[#ACTUAL].TiempoRecircBasica1 := #TiempoRecircBasica1;
#Informe[#ACTUAL].TempAcida := #TempAcida;
#Informe[#ACTUAL].CTLimpAcida := #CTLimpAcida;
#Informe[#ACTUAL].CTEnjAcida := #CTEnjAcida;
#Informe[#ACTUAL].PorcentajeQRefAcida := #PorcentajeQRefAcida;
#Informe[#ACTUAL].TiempoRecircAcida := #TiempoRecircAcida;
#Informe[#ACTUAL].TempBasica2 := #TempBasica2;
#Informe[#ACTUAL].CTLimpBasica2 := #CTLimpBasica2;
#Informe[#ACTUAL].CTEnjBasica2 := #CTEnjBasica2;
#Informe[#ACTUAL].PorcentajeQRefBasica2 := #PorcentajeQRefBasica2;
#Informe[#ACTUAL].TiempoRecircBasica2 := #TiempoRecircBasica2;
END_IF;
END_REGION DATOS_DURANTE_PRODUCCION
// ==========================================================================================================
REGION FIN_INFORME
// Se guardan los datos durante la produccion
IF #s_OrdenFinInforme THEN
// Guardado de fecha y hora actual
#Informe[#ACTUAL].FechaFin := "FC15005_ZC_DTL_TO_ZC_FECHA"(FechaDTL := #FechaHoraActual);
END_IF;
END_REGION FIN_INFORME
// ==========================================================================================================
REGION BORRADO_DATOS
// Borrado de todos los registros
IF #s_OrdenBorrarInforme THEN
IF #s_NumeroBorrado = -1 OR #s_NumeroBorrado = 0 THEN
FOR #for_i := 1 TO #t_IndiceSuperior DO
#Informe[#for_i] := #t_InformeVacio;
END_FOR;
ELSIF #s_NumeroBorrado >= 1 AND #s_NumeroBorrado < #t_IndiceSuperior THEN
// Borrado registro especifico
#Informe[#s_NumeroBorrado] := #t_InformeVacio;
// Desplazamiento de datos
#t_dummyInt := MOVE_BLK_VARIANT(SRC := #Informe,
COUNT := INT_TO_UDINT(#t_IndiceSuperior - #s_NumeroBorrado),
SRC_INDEX := #s_NumeroBorrado,
DEST_INDEX := #s_NumeroBorrado - 1,
DEST => #Informe);
// Borrado del ultimo registro
#Informe[#t_IndiceSuperior] := #t_InformeVacio;
ELSIF #s_NumeroBorrado = #t_IndiceSuperior THEN
// Borrado de ultimo registro
#Informe[#s_NumeroBorrado] := #t_InformeVacio;
END_IF;
#s_OrdenBorrarInforme := FALSE;
END_IF;
END_REGION BORRADO_DATOS
END_FUNCTION_BLOCK
@@ -0,0 +1,477 @@
FUNCTION "FC11_ZC_INF_GESTOR" : Void
TITLE = FC11_ZC_GESTOR_INFORMES
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para gestion de informes
VAR_INPUT
Pulso1Seg : Bool; // Pulso de 1 segundo
END_VAR
VAR_TEMP
for_i : Int; // Variable para bucles FOR
t_PosicionRegistro : Int; // Posicion del registro encontrado en el buffer
t_RegistroEncontrado : Bool; // Indicador de registro encontrado
t_RetVal : Int; // Variable para su uso en programa
END_VAR
VAR CONSTANT
REPOSO : Int := 0; // Etapa reposo
ESPERA_ENTRE_AVISOS : Int := 10; // Etapa espera entre avisos
LANZAR_AVISO : Int := 20; // Etapa lanzar aviso
ESPERA_CONFIRMACION : Int := 30; // Etapa espera confirmacion
FALLO : Int := 40; // Etapa fallo
BORRAR_DATOS : Int := 50; // Etapa borrar datos
END_VAR
BEGIN
REGION DESCRIPCION
(*
###### (C)Copyright ZeusControl 2024 - 2024
---
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
> **Restricciones:** Ninguna.
---
### Descripción Funcional
Función para la gestión e interfaz de escritura de informes hacia el sistema SCADA/HMI mediante una máquina de estados y la lectura de un buffer.
Las tareas que realiza son las siguientes:
- Extracción de datos del buffer: Búsqueda inversa del registro más antiguo pendiente de envío evaluando la condición `Registrar` = 1 mediante un bucle `FOR`.
- Transferencia del registro a la estructura estática `Gestion.Datos` y generación de un `Id` único basado en la fecha y hora actual.
- Limpieza inmediata de todas las variables (Booleanos, Reales, Enteros, Fechas y Strings) en la posición original del buffer.
- Ejecución de la etapa **REPOSO**: Activación de la secuencia ante la orden `OrdenIniciar`, siempre que se detecte conexión con el SCADA y la habilitación general esté activa.
- Ejecución de la etapa **ESPERA_ENTRE_AVISOS**: Temporización basada en pulsos de 1 segundo (`Pulso1Seg`) antes de un reintento. Deriva a fallo si se supera el máximo de intentos.
- Ejecución de la etapa **LANZAR_AVISO**: Activación de la señal `Aviso.Registrar` hacia el SCADA y paso a espera tras cumplirse el tiempo parametrizado.
- Ejecución de la etapa **ESPERA_CONFIRMACION**: Monitoreo de la respuesta del sistema superior. Si `Aviso.Estado` es igual a 4, el envío es exitoso. Si hay error (estado 12) o agotamiento de tiempo, se incrementa el contador de intentos y se repite el ciclo.
- Ejecución de la etapa **FALLO**: Generación de una traza de sistema (`TRZ_CAT_1_SYS`) registrando el código de error `TRZ_SYS_INF_ERR` ante la imposibilidad de entrega del informe.
- Ejecución de la etapa **BORRAR_DATOS**: Reseteo íntegro de la estructura `Gestion.Datos` para preparar el sistema para el próximo ciclo de comunicación y retorno al estado de reposo.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 30.07.2024 | (ABH) | Primera version. |
| 01.00.01 | 11.09.2024 | (ABH) | Se añade la generacion de ID con fecha y hora en el momento del guardar en SQL. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_INFORMES
// =============================================================================
// COPIAMOS EL ULTIMO REGISTRO (El mas antiguo) A LA VARIABLE REGISTRO QUE ES LA
// ENCARGADA DE COMUNICARLA CON EL SCADA/HMI
IF "DB11_INF".Gestion.HabilitacionGeneral AND "DB11_INF".Gestion.Datos.Registrar = 0 AND #Pulso1Seg THEN
// =============================================================================
// Buscamos el ultimo registro
FOR #for_i := "N_MAX_INF_BUFFER" TO 0 BY -1 DO
// =============================================================================
// Registro encontrado
IF "DB11_INF".Buffer[#for_i].Registrar = 1 THEN
#t_PosicionRegistro := #for_i;
#t_RegistroEncontrado := TRUE;
EXIT;
END_IF;
END_FOR;
// =============================================================================
// Si hemos encontrado el registro, copiamos a variable estatica
IF #t_RegistroEncontrado THEN
// =============================================================================
// Copiamos el los datos de la traza a variable estatica para tratarla
"DB11_INF".Gestion.Datos := "DB11_INF".Buffer[#t_PosicionRegistro];
"DB11_INF".Gestion.Datos.Id := "FC15006_ZC_DTL_TO_STRING"(FechaDTL := "DB1_SYS".FechaHoraActual, Formato := 3);
// =============================================================================
// Quitamos el registro del buffer
"DB11_INF".Buffer[#t_PosicionRegistro].Registrar := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Id := '';
"DB11_INF".Buffer[#t_PosicionRegistro].Tipo := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Codigo := '';
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_1.Ano := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_1.Mes := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_1.Dia := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_1.DiaSemana := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_1.Hora := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_1.Minuto := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_1.Segundo := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_1.NanoSegundo := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_2.Ano := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_2.Mes := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_2.Dia := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_2.DiaSemana := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_2.Hora := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_2.Minuto := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_2.Segundo := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_2.NanoSegundo := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_3.Ano := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_3.Mes := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_3.Dia := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_3.DiaSemana := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_3.Hora := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_3.Minuto := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_3.Segundo := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Fecha_3.NanoSegundo := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_1 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_2 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_3 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_4 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_5 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_6 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_7 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_8 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_9 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_10 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_11 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_12 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_13 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_14 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_15 := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_1 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_2 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_3 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_4 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_5 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_6 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_7 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_8 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_9 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_10 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_11 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_12 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_13 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_14 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_15 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_16 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_17 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_18 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_19 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_20 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_21 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_22 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_23 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_24 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_25 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_26 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_27 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_28 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_29 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_30 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_31 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Bool_32 := FALSE;
"DB11_INF".Buffer[#t_PosicionRegistro].Real_1 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_2 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_3 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_4 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_5 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_6 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_7 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_8 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_9 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_10 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_11 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_12 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_13 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_14 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_15 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_16 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_17 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_18 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_19 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Real_20 := 0.0;
"DB11_INF".Buffer[#t_PosicionRegistro].Int_1 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_2 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_3 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_4 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_5 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_6 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_7 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_8 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_9 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_10 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_11 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_12 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_13 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_14 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_15 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_16 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_17 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_18 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_19 :=
"DB11_INF".Buffer[#t_PosicionRegistro].Int_20 := 0;
END_IF;
END_IF;
// =============================================================================
// Si existe registro a trazar, lanzamos la orden de iniciar la secuencia
IF "DB11_INF".Gestion.Datos.Registrar = 1 AND "DB11_INF".Gestion.Etapa = #REPOSO AND "DB11_INF".Gestion.EstadoConexion THEN
// Lanzamos la orden de iniciar la secuencia de registro
"DB11_INF".Gestion.OrdenIniciar := TRUE;
END_IF;
END_REGION
// ==========================================================================================================
REGION SECUENCIA_INFORME
IF NOT "DB11_INF".Gestion.HabilitacionGeneral AND "DB11_INF".Gestion.Etapa <> #REPOSO THEN
"DB11_INF".Gestion.Etapa := #BORRAR_DATOS;
END_IF;
CASE "DB11_INF".Gestion.Etapa OF
// =============================================================================
#REPOSO:
IF "DB11_INF".Gestion.OrdenIniciar THEN
"DB11_INF".Gestion.Etapa := #LANZAR_AVISO;
"DB11_INF".Gestion.OrdenIniciar := FALSE;
"DB11_INF".Gestion.Aviso.Registrar := 0;
"DB11_INF".Gestion.TiempoActual := 0;
"DB11_INF".Gestion.Aviso.NumeroIntentosActual := 0;
END_IF;
// =============================================================================
#ESPERA_ENTRE_AVISOS:
// Contador de tiempo de etapa
IF #Pulso1Seg THEN
"DB11_INF".Gestion.TiempoActual += 1;
END_IF;
// Tiempo maximo de espera
IF ("DB11_INF".Gestion.TiempoActual >= "DB11_INF".Gestion.SP_Tiempo) THEN
"DB11_INF".Gestion.TiempoActual := 0;
"DB11_INF".Gestion.Etapa := #LANZAR_AVISO;
END_IF;
// Si ya se ha alcanzado el numero maximo de intentos, generamos la traza
IF "DB11_INF".Gestion.Aviso.NumeroIntentosActual >= "DB11_INF".Gestion.NumeroMaxIntentos THEN
"DB11_INF".Gestion.Etapa := #FALLO;
"DB11_INF".Gestion.Aviso.Registrar := 0;
END_IF;
// =============================================================================
#LANZAR_AVISO:
IF #Pulso1Seg THEN
"DB11_INF".Gestion.TiempoActual += 1;
END_IF;
"DB11_INF".Gestion.Aviso.Registrar := TRUE;
IF ("DB11_INF".Gestion.TiempoActual >= "DB11_INF".Gestion.SP_Tiempo) THEN
"DB11_INF".Gestion.Etapa := #ESPERA_CONFIRMACION;
END_IF;
// =============================================================================
#ESPERA_CONFIRMACION:
IF #Pulso1Seg THEN
"DB11_INF".Gestion.TiempoActual += 1;
END_IF;
// Confirmacion desde SCADA OK
IF "DB11_INF".Gestion.Aviso.Estado = 4 THEN
"DB11_INF".Gestion.TiempoActual := 0;
"DB11_INF".Gestion.Aviso.Estado := 0;
"DB11_INF".Gestion.Aviso.Registrar := FALSE;
"DB11_INF".Gestion.Etapa := #BORRAR_DATOS;
END_IF;
// Tiempo maximo de espera
IF ("DB11_INF".Gestion.TiempoActual >= "DB11_INF".Gestion.SP_Tiempo)
OR
("DB11_INF".Gestion.Aviso.Estado = 12)
THEN
"DB11_INF".Gestion.TiempoActual := 0;
"DB11_INF".Gestion.Aviso.Estado := 0;
"DB11_INF".Gestion.Aviso.Registrar := FALSE;
"DB11_INF".Gestion.Etapa := #ESPERA_ENTRE_AVISOS;
"DB11_INF".Gestion.Aviso.NumeroIntentosActual += 1;
END_IF;
// =============================================================================
#BORRAR_DATOS:
"DB11_INF".Gestion.Datos.Registrar := 0;
"DB11_INF".Gestion.Datos.Id := '';
"DB11_INF".Gestion.Datos.Tipo := 0;
"DB11_INF".Gestion.Datos.Codigo := '';
"DB11_INF".Gestion.Datos.Fecha_1.Ano := 0;
"DB11_INF".Gestion.Datos.Fecha_1.Mes := 0;
"DB11_INF".Gestion.Datos.Fecha_1.Dia := 0;
"DB11_INF".Gestion.Datos.Fecha_1.DiaSemana := 0;
"DB11_INF".Gestion.Datos.Fecha_1.Hora := 0;
"DB11_INF".Gestion.Datos.Fecha_1.Minuto := 0;
"DB11_INF".Gestion.Datos.Fecha_1.Segundo := 0;
"DB11_INF".Gestion.Datos.Fecha_1.NanoSegundo := 0;
"DB11_INF".Gestion.Datos.Fecha_2.Ano := 0;
"DB11_INF".Gestion.Datos.Fecha_2.Mes := 0;
"DB11_INF".Gestion.Datos.Fecha_2.Dia := 0;
"DB11_INF".Gestion.Datos.Fecha_2.DiaSemana := 0;
"DB11_INF".Gestion.Datos.Fecha_2.Hora := 0;
"DB11_INF".Gestion.Datos.Fecha_2.Minuto := 0;
"DB11_INF".Gestion.Datos.Fecha_2.Segundo := 0;
"DB11_INF".Gestion.Datos.Fecha_2.NanoSegundo := 0;
"DB11_INF".Gestion.Datos.Fecha_3.Ano := 0;
"DB11_INF".Gestion.Datos.Fecha_3.Mes := 0;
"DB11_INF".Gestion.Datos.Fecha_3.Dia := 0;
"DB11_INF".Gestion.Datos.Fecha_3.DiaSemana := 0;
"DB11_INF".Gestion.Datos.Fecha_3.Hora := 0;
"DB11_INF".Gestion.Datos.Fecha_3.Minuto := 0;
"DB11_INF".Gestion.Datos.Fecha_3.Segundo := 0;
"DB11_INF".Gestion.Datos.Fecha_3.NanoSegundo := 0;
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_1 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_2 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_3 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_4 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_5 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_6 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_7 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_8 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_9 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_10 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_11 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_12 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_13 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_14 :=
"DB11_INF".Buffer[#t_PosicionRegistro].CodString_15 := 0;
"DB11_INF".Gestion.Datos.Bool_1 :=
"DB11_INF".Gestion.Datos.Bool_2 :=
"DB11_INF".Gestion.Datos.Bool_3 :=
"DB11_INF".Gestion.Datos.Bool_4 :=
"DB11_INF".Gestion.Datos.Bool_5 :=
"DB11_INF".Gestion.Datos.Bool_6 :=
"DB11_INF".Gestion.Datos.Bool_7 :=
"DB11_INF".Gestion.Datos.Bool_8 :=
"DB11_INF".Gestion.Datos.Bool_9 :=
"DB11_INF".Gestion.Datos.Bool_10 :=
"DB11_INF".Gestion.Datos.Bool_11 :=
"DB11_INF".Gestion.Datos.Bool_12 :=
"DB11_INF".Gestion.Datos.Bool_13 :=
"DB11_INF".Gestion.Datos.Bool_14 :=
"DB11_INF".Gestion.Datos.Bool_15 :=
"DB11_INF".Gestion.Datos.Bool_16 :=
"DB11_INF".Gestion.Datos.Bool_17 :=
"DB11_INF".Gestion.Datos.Bool_18 :=
"DB11_INF".Gestion.Datos.Bool_19 :=
"DB11_INF".Gestion.Datos.Bool_20 :=
"DB11_INF".Gestion.Datos.Bool_21 :=
"DB11_INF".Gestion.Datos.Bool_22 :=
"DB11_INF".Gestion.Datos.Bool_23 :=
"DB11_INF".Gestion.Datos.Bool_24 :=
"DB11_INF".Gestion.Datos.Bool_25 :=
"DB11_INF".Gestion.Datos.Bool_26 :=
"DB11_INF".Gestion.Datos.Bool_27 :=
"DB11_INF".Gestion.Datos.Bool_28 :=
"DB11_INF".Gestion.Datos.Bool_29 :=
"DB11_INF".Gestion.Datos.Bool_30 :=
"DB11_INF".Gestion.Datos.Bool_31 :=
"DB11_INF".Gestion.Datos.Bool_32 := FALSE;
"DB11_INF".Gestion.Datos.Real_1 :=
"DB11_INF".Gestion.Datos.Real_2 :=
"DB11_INF".Gestion.Datos.Real_3 :=
"DB11_INF".Gestion.Datos.Real_4 :=
"DB11_INF".Gestion.Datos.Real_5 :=
"DB11_INF".Gestion.Datos.Real_6 :=
"DB11_INF".Gestion.Datos.Real_7 :=
"DB11_INF".Gestion.Datos.Real_8 :=
"DB11_INF".Gestion.Datos.Real_9 :=
"DB11_INF".Gestion.Datos.Real_10 :=
"DB11_INF".Gestion.Datos.Real_11 :=
"DB11_INF".Gestion.Datos.Real_12 :=
"DB11_INF".Gestion.Datos.Real_13 :=
"DB11_INF".Gestion.Datos.Real_14 :=
"DB11_INF".Gestion.Datos.Real_15 :=
"DB11_INF".Gestion.Datos.Real_16 :=
"DB11_INF".Gestion.Datos.Real_17 :=
"DB11_INF".Gestion.Datos.Real_18 :=
"DB11_INF".Gestion.Datos.Real_19 :=
"DB11_INF".Gestion.Datos.Real_20 := 0.0;
"DB11_INF".Gestion.Datos.Int_1 :=
"DB11_INF".Gestion.Datos.Int_2 :=
"DB11_INF".Gestion.Datos.Int_3 :=
"DB11_INF".Gestion.Datos.Int_4 :=
"DB11_INF".Gestion.Datos.Int_5 :=
"DB11_INF".Gestion.Datos.Int_6 :=
"DB11_INF".Gestion.Datos.Int_7 :=
"DB11_INF".Gestion.Datos.Int_8 :=
"DB11_INF".Gestion.Datos.Int_9 :=
"DB11_INF".Gestion.Datos.Int_10 :=
"DB11_INF".Gestion.Datos.Int_11 :=
"DB11_INF".Gestion.Datos.Int_12 :=
"DB11_INF".Gestion.Datos.Int_13 :=
"DB11_INF".Gestion.Datos.Int_14 :=
"DB11_INF".Gestion.Datos.Int_15 :=
"DB11_INF".Gestion.Datos.Int_16 :=
"DB11_INF".Gestion.Datos.Int_17 :=
"DB11_INF".Gestion.Datos.Int_18 :=
"DB11_INF".Gestion.Datos.Int_19 :=
"DB11_INF".Gestion.Datos.Int_20 := 0;
"DB11_INF".Gestion.Aviso.Registrar := 0;
"DB11_INF".Gestion.Etapa := #REPOSO;
// =============================================================================
#FALLO:
"FC8_ZC_TRZ_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRZ_CAT_1_SYS",
User := '',
CodInt_1 := "TRZ_SYS_INF_ERR",
CodInt_2 := 0,
CodInt_3 := 0,
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := 0.0,
CodReal_2 := 0.0);
"DB11_INF".Gestion.Datos.Registrar := 0;
"DB11_INF".Gestion.Etapa := #REPOSO;
END_CASE;
END_REGION
END_FUNCTION
@@ -0,0 +1,365 @@
FUNCTION "FC12_ZC_INF_REGISTRO" : Void
TITLE = FC12_ZC_INFORME_REGISTRO
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para almacenar una traza en el buffer de informes
VAR_INPUT
Registrar : USInt; // Orden registrar (0= no registrar / 1= registrar)
Tipo : Int; // Tipo
Codigo : String; // Codigo produccion
Fecha_1_Ano : UInt; // Fecha 1 Año
Fecha_1_Mes : USInt; // Fecha 1 Mes
Fecha_1_Dia : USInt; // Fecha 1 Dia
Fecha_1_DiaSemana : USInt; // Fecha 1 Dia de la semana
Fecha_1_Hora : USInt; // Fecha 1 Hora
Fecha_1_Minuto : USInt; // Fecha 1 Minuto
Fecha_1_Segundo : USInt; // Fecha 1 Segundo
Fecha_1_NanoSegundo : UDInt; // Fecha 1 Nanosegundo
Fecha_2_Ano : UInt; // Fecha 2 Año
Fecha_2_Mes : USInt; // Fecha 2 Mes
Fecha_2_Dia : USInt; // Fecha 2 Dia
Fecha_2_DiaSemana : USInt; // Fecha 2 Dia de la semana
Fecha_2_Hora : USInt; // Fecha 2 Hora
Fecha_2_Minuto : USInt; // Fecha 2 Minuto
Fecha_2_Segundo : USInt; // Fecha 2 Segundo
Fecha_2_NanoSegundo : UDInt; // Fecha 2 Nanosegundo
Fecha_3_Ano : UInt; // Fecha 3 Año
Fecha_3_Mes : USInt; // Fecha 3 Mes
Fecha_3_Dia : USInt; // Fecha 3 Dia
Fecha_3_DiaSemana : USInt; // Fecha 3 Dia de la semana
Fecha_3_Hora : USInt; // Fecha 3 Hora
Fecha_3_Minuto : USInt; // Fecha 3 Minuto
Fecha_3_Segundo : USInt; // Fecha 3 Segundo
Fecha_3_NanoSegundo : UDInt; // Fecha 3 Nanosegundo
Fecha_4_Ano : UInt; // Fecha 4 Año
Fecha_4_Mes : USInt; // Fecha 4 Mes
Fecha_4_Dia : USInt; // Fecha 4 Dia
Fecha_4_DiaSemana : USInt; // Fecha 4 Dia de la semana
Fecha_4_Hora : USInt; // Fecha 4 Hora
Fecha_4_Minuto : USInt; // Fecha 4 Minuto
Fecha_4_Segundo : USInt; // Fecha 4 Segundo
Fecha_4_NanoSegundo : UDInt; // Fecha 4 Nanosegundo
CodString_1 : Int; // Codigo string 1
CodString_2 : Int; // Codigo string 2
CodString_3 : Int; // Codigo string 3
CodString_4 : Int; // Codigo string 4
CodString_5 : Int; // Codigo string 5
CodString_6 : Int; // Codigo string 6
CodString_7 : Int; // Codigo string 7
CodString_8 : Int; // Codigo string 8
CodString_9 : Int; // Codigo string 9
CodString_10 : Int; // Codigo string 10
CodString_11 : Int; // Codigo string 11
CodString_12 : Int; // Codigo string 12
CodString_13 : Int; // Codigo string 13
CodString_14 : Int; // Codigo string 14
CodString_15 : Int; // Codigo string 15
Bool_1 : Bool; // Valor booleano 1
Bool_2 : Bool; // Valor booleano 2
Bool_3 : Bool; // Valor booleano 3
Bool_4 : Bool; // Valor booleano 4
Bool_5 : Bool; // Valor booleano 5
Bool_6 : Bool; // Valor booleano 6
Bool_7 : Bool; // Valor booleano 7
Bool_8 : Bool; // Valor booleano 8
Bool_9 : Bool; // Valor booleano 9
Bool_10 : Bool; // Valor booleano 10
Bool_11 : Bool; // Valor booleano 11
Bool_12 : Bool; // Valor booleano 12
Bool_13 : Bool; // Valor booleano 13
Bool_14 : Bool; // Valor booleano 14
Bool_15 : Bool; // Valor booleano 15
Bool_16 : Bool; // Valor booleano 16
Bool_17 : Bool; // Valor booleano 17
Bool_18 : Bool; // Valor booleano 18
Bool_19 : Bool; // Valor booleano 19
Bool_20 : Bool; // Valor booleano 20
Bool_21 : Bool; // Valor booleano 21
Bool_22 : Bool; // Valor booleano 22
Bool_23 : Bool; // Valor booleano 23
Bool_24 : Bool; // Valor booleano 24
Bool_25 : Bool; // Valor booleano 25
Bool_26 : Bool; // Valor booleano 26
Bool_27 : Bool; // Valor booleano 27
Bool_28 : Bool; // Valor booleano 28
Bool_29 : Bool; // Valor booleano 29
Bool_30 : Bool; // Valor booleano 30
Bool_31 : Bool; // Valor booleano 31
Bool_32 : Bool; // Valor booleano 32
Real_1 : Real; // Valor real 1
Real_2 : Real; // Valor real 2
Real_3 : Real; // Valor real 3
Real_4 : Real; // Valor real 4
Real_5 : Real; // Valor real 5
Real_6 : Real; // Valor real 6
Real_7 : Real; // Valor real 7
Real_8 : Real; // Valor real 8
Real_9 : Real; // Valor real 9
Real_10 : Real; // Valor real 10
Real_11 : Real; // Valor real 11
Real_12 : Real; // Valor real 12
Real_13 : Real; // Valor real 13
Real_14 : Real; // Valor real 14
Real_15 : Real; // Valor real 15
Real_16 : Real; // Valor real 16
Real_17 : Real; // Valor real 17
Real_18 : Real; // Valor real 18
Real_19 : Real; // Valor real 19
Real_20 : Real; // Valor real 20
Int_1 : Int; // Valor entero 1
Int_2 : Int; // Valor entero 2
Int_3 : Int; // Valor entero 3
Int_4 : Int; // Valor entero 4
Int_5 : Int; // Valor entero 5
Int_6 : Int; // Valor entero 6
Int_7 : Int; // Valor entero 7
Int_8 : Int; // Valor entero 8
Int_9 : Int; // Valor entero 9
Int_10 : Int; // Valor entero 10
Int_11 : Int; // Valor entero 11
Int_12 : Int; // Valor entero 12
Int_13 : Int; // Valor entero 13
Int_14 : Int; // Valor entero 14
Int_15 : Int; // Valor entero 15
Int_16 : Int; // Valor entero 16
Int_17 : Int; // Valor entero 17
Int_18 : Int; // Valor entero 18
Int_19 : Int; // Valor entero 19
Int_20 : Int; // Valor entero 20
END_VAR
VAR_TEMP
t_dummy : Int;
t_fecha_vacia {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
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 para el almacenamiento y estructuración masiva de registros de trazabilidad dentro del buffer de informes del sistema.
En ella se realizan las siguientes acciones:
- Comprobación de habilitación: Verificación de la variable de permiso `HabilitacionGeneral`; si no se encuentra activa, se omite la ejecución del bloque mediante un retorno anticipado.
- Desplazamiento de datos (FIFO):
- Ejecución de la instrucción de bloque para desplazar todos los registros existentes en el Buffer una posición hacia abajo.
- Liberación del índice 0 del array para permitir la inserción del nuevo registro sin sobrescribir el histórico reciente.
- Gestión e inserción del nuevo registro:
- Generación y asignación de un identificador único (Id) transformando la fecha y hora actual del sistema a formato cadena.
- Escritura de los metadatos de control y catalogación (Registrar, Tipo, Codigo) en la primera posición del buffer.
- Procesamiento y validación de hasta 4 marcas de tiempo dinámicas (Fecha_1 a Fecha_4), forzando una fecha por defecto (01/01/1970) si el año recibido es 0.
- Mapeo y transferencia masiva de todas las variables de proceso vinculadas al evento, incluyendo cadenas de texto (CodString_1 a 15), booleanos (Bool_1 a 32), reales (Real_1 a 20) y enteros (Int_1 a 20).
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.01 | 30.07.2024 | (ABH) | Primera version. |
| 01.00.02 | 11.09.2024 | (ABH) | Se elimina la generacion de ID, para que lo genere el gestor. |
| 01.00.03 | 06.11.2025 | (ABH) | Fix escritura de hora si año seleccionado es 0. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION COMPROBACION_HABILITACION
// Si no esta habilitado los informes, salimos de la funcion.
IF NOT "DB11_INF".Gestion.HabilitacionGeneral THEN
RETURN;
END_IF;
END_REGION COMPROBACION_HABILITACION
// ==========================================================================================================
REGION DESPLAZAMIENTO_DE_DATOS
// Desplazamos los datos del buffer una posicion hacia abajo
#t_dummy := MOVE_BLK_VARIANT(SRC := "DB11_INF".Buffer, COUNT := INT_TO_UDINT(("N_MAX_INF_BUFFER")), SRC_INDEX := 0, DEST_INDEX := 1, DEST => "DB11_INF".Buffer);
END_REGION DESPLAZAMIENTO_DE_DATOS
// ==========================================================================================================
REGION GESTION_REGISTRO_INFORME
// Grabamos el nuevo registro
"DB11_INF".Buffer[0].Registrar := #Registrar;
"DB11_INF".Buffer[0].Id := "FC15006_ZC_DTL_TO_STRING"(FechaDTL := "DB1_SYS".FechaHoraActual, Formato := 3);
"DB11_INF".Buffer[0].Tipo := #Tipo;
"DB11_INF".Buffer[0].Codigo := #Codigo;
IF #Fecha_1_Ano = 0 THEN
"DB11_INF".Buffer[0].Fecha_1.Ano := 1970;
"DB11_INF".Buffer[0].Fecha_1.Mes := 1;
"DB11_INF".Buffer[0].Fecha_1.Dia := 1;
"DB11_INF".Buffer[0].Fecha_1.DiaSemana := 5;
ELSE
"DB11_INF".Buffer[0].Fecha_1.Ano := #Fecha_1_Ano;
"DB11_INF".Buffer[0].Fecha_1.Mes := #Fecha_1_Mes;
"DB11_INF".Buffer[0].Fecha_1.Dia := #Fecha_1_Dia;
"DB11_INF".Buffer[0].Fecha_1.DiaSemana := #Fecha_1_DiaSemana;
END_IF;
"DB11_INF".Buffer[0].Fecha_1.Hora := #Fecha_1_Hora;
"DB11_INF".Buffer[0].Fecha_1.Minuto := #Fecha_1_Minuto;
"DB11_INF".Buffer[0].Fecha_1.Segundo := #Fecha_1_Segundo;
"DB11_INF".Buffer[0].Fecha_1.NanoSegundo := #Fecha_1_NanoSegundo;
IF #Fecha_2_Ano = 0 THEN
"DB11_INF".Buffer[0].Fecha_2.Ano := 1970;
"DB11_INF".Buffer[0].Fecha_2.Mes := 1;
"DB11_INF".Buffer[0].Fecha_2.Dia := 1;
"DB11_INF".Buffer[0].Fecha_2.DiaSemana := 5;
ELSE
"DB11_INF".Buffer[0].Fecha_2.Ano := #Fecha_2_Ano;
"DB11_INF".Buffer[0].Fecha_2.Mes := #Fecha_2_Mes;
"DB11_INF".Buffer[0].Fecha_2.Dia := #Fecha_2_Dia;
"DB11_INF".Buffer[0].Fecha_2.DiaSemana := #Fecha_2_DiaSemana;
END_IF;
"DB11_INF".Buffer[0].Fecha_2.Hora := #Fecha_2_Hora;
"DB11_INF".Buffer[0].Fecha_2.Minuto := #Fecha_2_Minuto;
"DB11_INF".Buffer[0].Fecha_2.Segundo := #Fecha_2_Segundo;
"DB11_INF".Buffer[0].Fecha_2.NanoSegundo := #Fecha_2_NanoSegundo;
IF #Fecha_3_Ano = 0 THEN
"DB11_INF".Buffer[0].Fecha_3.Ano := 1970;
"DB11_INF".Buffer[0].Fecha_3.Mes := 1;
"DB11_INF".Buffer[0].Fecha_3.Dia := 1;
"DB11_INF".Buffer[0].Fecha_3.DiaSemana := 5;
ELSE
"DB11_INF".Buffer[0].Fecha_3.Ano := #Fecha_3_Ano;
"DB11_INF".Buffer[0].Fecha_3.Mes := #Fecha_3_Mes;
"DB11_INF".Buffer[0].Fecha_3.Dia := #Fecha_3_Dia;
"DB11_INF".Buffer[0].Fecha_3.DiaSemana := #Fecha_3_DiaSemana;
END_IF;
IF #Fecha_4_Ano = 0 THEN
"DB11_INF".Buffer[0].Fecha_4.Ano := 1970;
"DB11_INF".Buffer[0].Fecha_4.Mes := 1;
"DB11_INF".Buffer[0].Fecha_4.Dia := 1;
"DB11_INF".Buffer[0].Fecha_4.DiaSemana := 5;
ELSE
"DB11_INF".Buffer[0].Fecha_4.Ano := #Fecha_4_Ano;
"DB11_INF".Buffer[0].Fecha_4.Mes := #Fecha_4_Mes;
"DB11_INF".Buffer[0].Fecha_4.Dia := #Fecha_4_Dia;
"DB11_INF".Buffer[0].Fecha_4.DiaSemana := #Fecha_4_DiaSemana;
END_IF;
"DB11_INF".Buffer[0].Fecha_3.Hora := #Fecha_3_Hora;
"DB11_INF".Buffer[0].Fecha_3.Minuto := #Fecha_3_Minuto;
"DB11_INF".Buffer[0].Fecha_3.Segundo := #Fecha_3_Segundo;
"DB11_INF".Buffer[0].Fecha_3.NanoSegundo := #Fecha_3_NanoSegundo;
"DB11_INF".Buffer[0].CodString_1 := #CodString_1;
"DB11_INF".Buffer[0].CodString_2 := #CodString_2;
"DB11_INF".Buffer[0].CodString_3 := #CodString_3;
"DB11_INF".Buffer[0].CodString_4 := #CodString_4;
"DB11_INF".Buffer[0].CodString_5 := #CodString_5;
"DB11_INF".Buffer[0].CodString_6 := #CodString_6;
"DB11_INF".Buffer[0].CodString_7 := #CodString_7;
"DB11_INF".Buffer[0].CodString_8 := #CodString_8;
"DB11_INF".Buffer[0].CodString_9 := #CodString_9;
"DB11_INF".Buffer[0].CodString_10 := #CodString_10;
"DB11_INF".Buffer[0].CodString_11 := #CodString_11;
"DB11_INF".Buffer[0].CodString_12 := #CodString_12;
"DB11_INF".Buffer[0].CodString_13 := #CodString_13;
"DB11_INF".Buffer[0].CodString_14 := #CodString_14;
"DB11_INF".Buffer[0].CodString_15 := #CodString_15;
"DB11_INF".Buffer[0].Bool_1 := #Bool_1;
"DB11_INF".Buffer[0].Bool_2 := #Bool_2;
"DB11_INF".Buffer[0].Bool_3 := #Bool_3;
"DB11_INF".Buffer[0].Bool_4 := #Bool_4;
"DB11_INF".Buffer[0].Bool_5 := #Bool_5;
"DB11_INF".Buffer[0].Bool_6 := #Bool_6;
"DB11_INF".Buffer[0].Bool_7 := #Bool_7;
"DB11_INF".Buffer[0].Bool_8 := #Bool_8;
"DB11_INF".Buffer[0].Bool_9 := #Bool_9;
"DB11_INF".Buffer[0].Bool_10 := #Bool_10;
"DB11_INF".Buffer[0].Bool_11 := #Bool_11;
"DB11_INF".Buffer[0].Bool_12 := #Bool_12;
"DB11_INF".Buffer[0].Bool_13 := #Bool_13;
"DB11_INF".Buffer[0].Bool_14 := #Bool_14;
"DB11_INF".Buffer[0].Bool_15 := #Bool_15;
"DB11_INF".Buffer[0].Bool_16 := #Bool_16;
"DB11_INF".Buffer[0].Bool_17 := #Bool_17;
"DB11_INF".Buffer[0].Bool_18 := #Bool_18;
"DB11_INF".Buffer[0].Bool_19 := #Bool_19;
"DB11_INF".Buffer[0].Bool_20 := #Bool_20;
"DB11_INF".Buffer[0].Bool_21 := #Bool_21;
"DB11_INF".Buffer[0].Bool_22 := #Bool_22;
"DB11_INF".Buffer[0].Bool_23 := #Bool_23;
"DB11_INF".Buffer[0].Bool_24 := #Bool_24;
"DB11_INF".Buffer[0].Bool_25 := #Bool_25;
"DB11_INF".Buffer[0].Bool_26 := #Bool_26;
"DB11_INF".Buffer[0].Bool_27 := #Bool_27;
"DB11_INF".Buffer[0].Bool_28 := #Bool_28;
"DB11_INF".Buffer[0].Bool_29 := #Bool_29;
"DB11_INF".Buffer[0].Bool_30 := #Bool_30;
"DB11_INF".Buffer[0].Bool_31 := #Bool_31;
"DB11_INF".Buffer[0].Bool_32 := #Bool_32;
"DB11_INF".Buffer[0].Real_1 := #Real_1;
"DB11_INF".Buffer[0].Real_2 := #Real_2;
"DB11_INF".Buffer[0].Real_3 := #Real_3;
"DB11_INF".Buffer[0].Real_4 := #Real_4;
"DB11_INF".Buffer[0].Real_5 := #Real_5;
"DB11_INF".Buffer[0].Real_6 := #Real_6;
"DB11_INF".Buffer[0].Real_7 := #Real_7;
"DB11_INF".Buffer[0].Real_8 := #Real_8;
"DB11_INF".Buffer[0].Real_9 := #Real_9;
"DB11_INF".Buffer[0].Real_10 := #Real_10;
"DB11_INF".Buffer[0].Real_11 := #Real_11;
"DB11_INF".Buffer[0].Real_12 := #Real_12;
"DB11_INF".Buffer[0].Real_13 := #Real_13;
"DB11_INF".Buffer[0].Real_14 := #Real_14;
"DB11_INF".Buffer[0].Real_15 := #Real_15;
"DB11_INF".Buffer[0].Real_16 := #Real_16;
"DB11_INF".Buffer[0].Real_17 := #Real_17;
"DB11_INF".Buffer[0].Real_18 := #Real_18;
"DB11_INF".Buffer[0].Real_19 := #Real_19;
"DB11_INF".Buffer[0].Real_20 := #Real_20;
"DB11_INF".Buffer[0].Int_1 := #Int_1;
"DB11_INF".Buffer[0].Int_2 := #Int_2;
"DB11_INF".Buffer[0].Int_3 := #Int_3;
"DB11_INF".Buffer[0].Int_4 := #Int_4;
"DB11_INF".Buffer[0].Int_5 := #Int_5;
"DB11_INF".Buffer[0].Int_6 := #Int_6;
"DB11_INF".Buffer[0].Int_7 := #Int_7;
"DB11_INF".Buffer[0].Int_8 := #Int_8;
"DB11_INF".Buffer[0].Int_9 := #Int_9;
"DB11_INF".Buffer[0].Int_10 := #Int_10;
"DB11_INF".Buffer[0].Int_11 := #Int_11;
"DB11_INF".Buffer[0].Int_12 := #Int_12;
"DB11_INF".Buffer[0].Int_13 := #Int_13;
"DB11_INF".Buffer[0].Int_14 := #Int_14;
"DB11_INF".Buffer[0].Int_15 := #Int_15;
"DB11_INF".Buffer[0].Int_16 := #Int_16;
"DB11_INF".Buffer[0].Int_17 := #Int_17;
"DB11_INF".Buffer[0].Int_18 := #Int_18;
"DB11_INF".Buffer[0].Int_19 := #Int_19;
"DB11_INF".Buffer[0].Int_20 := #Int_20;
END_REGION GESTION_REGISTRO_INFORME
END_FUNCTION
@@ -0,0 +1,135 @@
FUNCTION "FC15_ZC_GEST_PREAL" : Void
TITLE = FC15_ZC_GEST_PREAL
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
VAR_INPUT
PrimerCiclo : Bool; // Marca de primer ciclo de arranque de la CPU
FechaHora {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
Usuario : String; // Usuario actual
PReal : Array[*] of "UDT_ZC_PREAL"; // Array de parametro real
IndexTraza : Int; // Indice de texto parametro para trazabilidad
END_VAR
VAR_IN_OUT
ValorAnterior : Array[*] of Real; // Array de valores anteriores
END_VAR
VAR_TEMP
for_i : DInt; // Variable para bucles FOR
t_IndiceSuperior : DInt; // Indice superior del array
t_IndiceInferior : DInt; // Indice inferior del array
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:** DB Optimizado.
---
### Descripción Funcional
Función principal para la gestión de trazabilidad de los parámetros **reales** del proceso.
Su objetivo principal es detectar cambios de valor en el array de parámetros y lanzar un registro de traza cuando esto sucede.
El bloque opera de la siguiente manera:
1. Calcula dinámicamente los límites superior e inferior del array de entrada.
2. Si es el primer ciclo de ejecución de la CPU, inicializa los valores "anteriores" con los valores "actuales" para evitar falsos registros de traza iniciales.
3. En ciclos normales, compara el valor actual de cada parámetro con su valor guardado en el ciclo anterior.
4. Si detecta una diferencia, llama a la función `FC8_ZC_TRAZA_REGISTRO` para registrar el cambio y actualiza el valor anterior.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_PREAL` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 2026-03-20 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION CALCULO_LIMITES_ARRAY
// Calculo de los limites del array, ya que la entrada del array de parametros es variable.
#t_IndiceInferior := LOWER_BOUND(ARR := #PReal, DIM := 1);
#t_IndiceSuperior := UPPER_BOUND(ARR := #PReal, DIM := 1);
END_REGION CALCULO_LIMITES_ARRAY
// ==========================================================================================================
REGION PRIMER_ARRANQUE
// En el primer arranque de la CPU, recorremos el array completo y almacenamos los valores actuales como valores anteriores en el arranque de la CPU.
// Esto se realiza ya que los valores anteriores no son remanentes, para optimizar la memoria del PLC.
IF #PrimerCiclo THEN
FOR #for_i := #t_IndiceInferior TO #t_IndiceSuperior DO
#ValorAnterior[#for_i] := #PReal[#for_i].Valor;
END_FOR;
// Salimos inmediatamente de la funcion, para realizar la gestion de los parametros con la CPU arrancada
RETURN;
END_IF;
END_REGION PRIMER_ARRANQUE
// ==========================================================================================================
REGION GESTION_PARAMETROS
// Gestion de los cambios de valor de los parametros. Si detecta un cambio, lanza la traza
// y actualiza el valor anterior.
FOR #for_i := #t_IndiceInferior TO #t_IndiceSuperior DO
// En caso de detectar cambio de valor
IF #PReal[#for_i].Valor <> #ValorAnterior[#for_i] THEN
// Lanzamos la trazabilidad
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHora,
Registrar := 1,
Categoria := "TRZ_CAT_25_PREAL",
User := #Usuario,
CodInt_1 := #IndexTraza + DINT_TO_INT(#for_i - 1),
CodInt_2 := "TRZ_SYS_PAR_VAL_ANT",
CodInt_3 := "TRZ_SYS_PAR_VAL_NUE",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := #ValorAnterior[#for_i],
CodReal_2 := #PReal[#for_i].Valor);
// Actualizamos el valor anterior
#ValorAnterior[#for_i] := #PReal[#for_i].Valor;
END_IF;
END_FOR;
END_REGION GESTION_PARAMETROS
END_FUNCTION
@@ -0,0 +1,135 @@
FUNCTION "FC16_ZC_GEST_PINT" : Void
TITLE = FC16_ZC_GEST_PINT
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
VAR_INPUT
PrimerCiclo : Bool; // Marca de primer ciclo de arranque de la CPU
FechaHora {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
Usuario : String; // Usuario actual
PInt : Array[*] of "UDT_ZC_PINT"; // Array de parametro entero
IndexTraza : Int; // Indice de texto parametro para trazabilidad
END_VAR
VAR_IN_OUT
ValorAnterior : Array[*] of Int; // Array de valores anteriores
END_VAR
VAR_TEMP
for_i : DInt; // Variable para bucles FOR
t_IndiceSuperior : DInt; // Indice superior del array
t_IndiceInferior : DInt; // Indice inferior del array
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:** DB Optimizado.
---
### Descripción Funcional
Función principal para la gestión de trazabilidad de los parámetros **enteros** del proceso.
Su objetivo principal es detectar cambios de valor en el array de parámetros y lanzar un registro de traza cuando esto sucede.
El bloque opera de la siguiente manera:
1. Calcula dinámicamente los límites superior e inferior del array de entrada.
2. Si es el primer ciclo de ejecución de la CPU, inicializa los valores "anteriores" con los valores "actuales" para evitar falsos registros de traza iniciales.
3. En ciclos normales, compara el valor actual de cada parámetro con su valor guardado en el ciclo anterior.
4. Si detecta una diferencia, llama a la función `FC8_ZC_TRAZA_REGISTRO` para registrar el cambio y actualiza el valor anterior.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC8_ZC_TRAZA_REGISTRO` |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_PINT` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 2026-03-20 | (ABH) | Primera version. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION CALCULO_LIMITES_ARRAY
// Calculo de los limites del array, ya que la entrada del array de parametros es variable.
#t_IndiceInferior := LOWER_BOUND(ARR := #PInt, DIM := 1);
#t_IndiceSuperior := UPPER_BOUND(ARR := #PInt, DIM := 1);
END_REGION CALCULO_LIMITES_ARRAY
// ==========================================================================================================
REGION PRIMER_ARRANQUE
// En el primer arranque de la CPU, recorremos el array completo y almacenamos los valores actuales como valores anteriores en el arranque de la CPU.
// Esto se realiza ya que los valores anteriores no son remanentes, para optimizar la memoria del PLC.
IF #PrimerCiclo THEN
FOR #for_i := #t_IndiceInferior TO #t_IndiceSuperior DO
#ValorAnterior[#for_i] := #PInt[#for_i].Valor;
END_FOR;
// Salimos inmediatamente de la funcion, para realizar la gestion de los parametros con la CPU arrancada
RETURN;
END_IF;
END_REGION PRIMER_ARRANQUE
// ==========================================================================================================
REGION GESTION_PARAMETROS
// Gestion de los cambios de valor de los parametros. Si detecta un cambio, lanza la traza
// y actualiza el valor anterior.
FOR #for_i := #t_IndiceInferior TO #t_IndiceSuperior DO
// En caso de detectar cambio de valor
IF #PInt[#for_i].Valor <> #ValorAnterior[#for_i] THEN
// Lanzamos la trazabilidad
"FC8_ZC_TRZ_REGISTRO"(FechaHora := #FechaHora,
Registrar := 1,
Categoria := "TRZ_CAT_26_PINT",
User := #Usuario,
CodInt_1 := #IndexTraza + DINT_TO_INT(#for_i - 1),
CodInt_2 := "TRZ_SYS_PAR_VAL_ANT",
CodInt_3 := "TRZ_SYS_PAR_VAL_NUE",
CodInt_4 := 0,
CodInt_5 := 0,
CodReal_1 := #ValorAnterior[#for_i],
CodReal_2 := #PInt[#for_i].Valor);
// Actualizamos el valor anterior
#ValorAnterior[#for_i] := #PInt[#for_i].Valor;
END_IF;
END_FOR;
END_REGION GESTION_PARAMETROS
END_FUNCTION
@@ -0,0 +1,269 @@
FUNCTION_BLOCK "FB15200_ZC_SYNC_RECETA_PANEL"
TITLE = FB15200_ZC_SYNC_RECETA_PANEL
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Sincronizacion de recetas desde Scada (BBDD) a paneles
VAR_INPUT
HabilitarBorrado : Bool := FALSE; // Habilita el borrado de ficheros
RutaCarpetaRecetas { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : String[25] := '\temp\RecetasScada\';
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
TiempoRetardoInicio : Time := T#20s;
TiempoRetardoHmi : Time := T#10s;
TiempoComprobacion : Time := T#500ms;
TiempoError : Time := T#8s;
END_VAR
VAR_OUTPUT RETAIN
ErrorImportar : Bool;
ErrorBorrar : Bool;
END_VAR
VAR RETAIN
s_ExisteRecetaNueva : Bool;
s_OrdenSincHMI : Bool;
s_OrdenBorrarHMI : Bool;
s_EstadoImportar : Int;
s_EstadoBorrar : Int;
s_RutaCarpeta : String;
s_Iniciar { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
s_Etapa { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
s_EtapaSiguiente { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
UltimaSincronizacion {InstructionName := 'DTL'; LibVersion := '1.0'; S7_SetPoint := 'True'} : DTL;
END_VAR
VAR
TON_OrdenInicio {InstructionName := 'TON_TIME'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : TON_TIME;
TON_RetardoImportarHmi {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
TON_RetardoBorrarHmi {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
TON_EstadoImportar {InstructionName := 'TON_TIME'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'; S7_SetPoint := 'False'} : TON_TIME;
TON_ErrorImportar {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
TON_EstadoBorrar {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
TON_ErrorBorrar {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
END_VAR
VAR CONSTANT
ETAPA_0_REPOSO : Int := 0;
ETAPA_10_IMPORTAR_ORDEN_HMI : Int := 10;
ETAPA_20_IMPORTAR_ESPERA_HMI : Int := 20;
ETAPA_30_IMPORTAR_COMPROBACION : Int := 30;
ETAPA_40_BORRAR_ORDEN_HMI : Int := 40;
ETAPA_50_BORRAR_ESPERA_HMI : Int := 50;
ETAPA_60_BORRAR_COMPROBACION : Int := 60;
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 |
---
### Descripción Funcional
Función central para la gestión de la sincronización de recetas con el HMI. Implementa una máquina de estados que coordina la importación (y borrado opcional) de recetas desde el HMI, gestionando las temporizaciones de espera, las señales de confirmación y los códigos de estado/error, así como el registro de la última sincronización correcta.
> **Restricciones:** Ninguna.
Acciones detalladas:
- Gestiona los temporizadores generales del bloque: retardo de inicio (`TON_OrdenInicio`), retardos de orden de importar/borrar desde HMI (`TON_RetardoImportarHmi`, `TON_RetardoBorrarHmi`), temporizadores de espera de estado (`TON_EstadoImportar`, `TON_EstadoBorrar`) y temporizadores de error (`TON_ErrorImportar`, `TON_ErrorBorrar`).
- Estado `ETAPA_0_REPOSO`: si existe receta nueva (`s_ExisteRecetaNueva`) y la etapa actual es reposo, arranca la temporización de inicio. Una vez cumplido el tiempo, se activa `s_Iniciar`, se inicializan los estados de importar/borrar, se carga la `RutaCarpetaRecetas` en `s_RutaCarpeta` y se pasa a la etapa de orden de importar al HMI.
- Estado `ETAPA_10_IMPORTAR_ORDEN_HMI`: activa la marca `s_OrdenSincHMI` cuando se recibe desde HMI o cuando expira `TON_RetardoImportarHmi`, y transiciona a la etapa de espera de importación.
- Estado `ETAPA_20_IMPORTAR_ESPERA_HMI`: espera a que finalice la temporización de estado (`TON_EstadoImportar`) o de error (`TON_ErrorImportar`) para pasar a la etapa de comprobación.
- Estado `ETAPA_30_IMPORTAR_COMPROBACION`: evalúa el código de estado de importación (`s_EstadoImportar`). Si es correcto (`4`), resetea `s_ExisteRecetaNueva` y `ErrorImportar`, registra la `UltimaSincronizacion` y, si está habilitado el borrado (`HabilitarBorrado`), continúa con la etapa de borrado; en caso contrario, vuelve a `ETAPA_0_REPOSO`. Si el estado no es correcto, marca `ErrorImportar` y vuelve a reposo.
- Estado `ETAPA_40_BORRAR_ORDEN_HMI`: activa `s_OrdenBorrarHMI` cuando se recibe desde HMI o cuando expira `TON_RetardoBorrarHmi`, y transiciona a la etapa de espera de borrado.
- Estado `ETAPA_50_BORRAR_ESPERA_HMI`: espera a que finalice la temporización de estado (`TON_EstadoBorrar`) o de error (`TON_ErrorBorrar`) para pasar a la etapa de comprobación de borrado.
- Estado `ETAPA_60_BORRAR_COMPROBACION`: evalúa el código de estado de borrado (`s_EstadoBorrar`). Si es correcto (`4`), resetea `s_ExisteRecetaNueva` y `ErrorBorrar`; si no, activa `ErrorBorrar`. En ambos casos, resetea los flags de estado y orden y vuelve a `ETAPA_0_REPOSO`.
- En cada ciclo se actualiza la etapa en curso (`s_Etapa := s_EtapaSiguiente`), manteniendo la coherencia de la máquina de estados.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 03.11.2022 | (ABH) | Primera versión. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_TEMPORIZADORES
// Retardo inicio sincronizacion
#TON_OrdenInicio(IN := #s_ExisteRecetaNueva AND (#s_Etapa = #ETAPA_0_REPOSO),
PT := #TiempoRetardoInicio);
IF #TON_OrdenInicio.Q THEN
#s_Iniciar := true;
END_IF;
// Retardo orden importar hmi
#TON_RetardoImportarHmi(IN := (#s_Etapa = #ETAPA_10_IMPORTAR_ORDEN_HMI),
PT := #TiempoRetardoHmi);
// Retardo estado/error importar
#TON_EstadoImportar(IN := (#s_Etapa = #ETAPA_20_IMPORTAR_ESPERA_HMI) AND ((#s_EstadoImportar = 4) OR (#s_EstadoImportar = 12)),
PT := #TiempoComprobacion);
#TON_ErrorImportar(IN := (#s_Etapa = #ETAPA_20_IMPORTAR_ESPERA_HMI),
PT := #TiempoError);
// Retardo orden borrar hmi
#TON_RetardoBorrarHmi(IN := (#s_Etapa = #ETAPA_40_BORRAR_ORDEN_HMI),
PT := #TiempoRetardoHmi);
// Retardo borrar estado
#TON_EstadoBorrar(IN := (#s_Etapa = #ETAPA_50_BORRAR_ESPERA_HMI) AND ((#s_EstadoImportar = 4) OR (#s_EstadoImportar = 12)),
PT := #TiempoComprobacion);
#TON_ErrorBorrar(IN := (#s_Etapa = #ETAPA_50_BORRAR_ESPERA_HMI),
PT := #TiempoError);
END_REGION GESTION_TEMPORIZADORES
// ==========================================================================================================
REGION LOGICA
CASE #s_Etapa OF
#ETAPA_0_REPOSO:
#s_RutaCarpeta := '';
REGION REPOSO
IF #s_Iniciar THEN
#s_Iniciar := FALSE;
#s_EstadoImportar := 0;
#s_EstadoBorrar := 0;
#s_RutaCarpeta := #RutaCarpetaRecetas;
#s_EtapaSiguiente := #ETAPA_10_IMPORTAR_ORDEN_HMI;
END_IF;
END_REGION
#ETAPA_10_IMPORTAR_ORDEN_HMI:
REGION IMPORTAR_ORDEN_HMI
IF #s_OrdenSincHMI THEN
#s_EtapaSiguiente := #ETAPA_20_IMPORTAR_ESPERA_HMI;
END_IF;
IF #TON_RetardoImportarHmi.Q THEN
#s_OrdenSincHMI := TRUE;
END_IF;
END_REGION
#ETAPA_20_IMPORTAR_ESPERA_HMI:
REGION IMPORTAR_ESPERA_HMI
IF #TON_EstadoImportar.Q OR #TON_ErrorImportar.Q THEN
#s_EtapaSiguiente := #ETAPA_30_IMPORTAR_COMPROBACION;
END_IF;
END_REGION
#ETAPA_30_IMPORTAR_COMPROBACION:
REGION IMPORTAR_COMPROBACION
// Si se ejecuta correctamente, reseteamos marca que indica que hay recetas nuevas que sincronizar
// y guardamos la fecha y hora de la ultima sincronizacion
IF #s_EstadoImportar = 4 THEN
#s_ExisteRecetaNueva :=
#ErrorImportar := FALSE;
#UltimaSincronizacion := #FechaHoraActual;
IF #HabilitarBorrado THEN
#s_EtapaSiguiente := #ETAPA_40_BORRAR_ORDEN_HMI;
ELSE
#s_EtapaSiguiente := #ETAPA_0_REPOSO;
END_IF;
ELSE
#ErrorImportar := TRUE;
#s_EtapaSiguiente := #ETAPA_0_REPOSO;
END_IF;
#s_OrdenSincHMI := FALSE;
#s_EstadoImportar := 0;
END_REGION
#ETAPA_40_BORRAR_ORDEN_HMI:
REGION BORRAR_ORDEN_HMI
IF #s_OrdenBorrarHMI THEN
#s_EtapaSiguiente := #ETAPA_50_BORRAR_ESPERA_HMI;
END_IF;
IF #TON_RetardoBorrarHmi.Q THEN
#s_OrdenBorrarHMI := TRUE;
END_IF;
END_REGION
#ETAPA_50_BORRAR_ESPERA_HMI:
REGION BORRAR_ESPERA_HMI
IF #TON_EstadoBorrar.Q OR #TON_ErrorBorrar.Q THEN
#s_EtapaSiguiente := #ETAPA_60_BORRAR_COMPROBACION;
END_IF;
END_REGION
#ETAPA_60_BORRAR_COMPROBACION:
REGION IMPORTAR_COMPROBACION
// Si se ejecuta correctamente, reseteamos marca que indica que hay recetas nuevas que sincronizar
// y guardamos la fecha y hora de la ultima sincronizacion
IF #s_EstadoBorrar = 4 THEN
#s_ExisteRecetaNueva :=
#ErrorBorrar := FALSE;
ELSE
#ErrorBorrar := TRUE;
END_IF;
#s_EstadoBorrar := 0;
#s_OrdenBorrarHMI := FALSE;
#s_EtapaSiguiente := #ETAPA_0_REPOSO;
END_REGION
ELSE
#s_EtapaSiguiente := #ETAPA_0_REPOSO;
END_CASE;
#s_Etapa := #s_EtapaSiguiente;
END_REGION LOGICA
END_FUNCTION_BLOCK
@@ -0,0 +1,268 @@
FUNCTION_BLOCK "FB15050_ZC_CHEQUEO"
TITLE = FB15050_ZC_CHEQUEO
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
VAR_INPUT
GuardarRegistro : Bool; // Orden guardado registro nuevo
ComprobarBajoChequeo : Bool; // Orden comprobacion Alarma bajo chequeo
ResetBajoChequeo : Bool; // Reset alarma bajo chequeo
TomarDatoChequeo : Bool; // Orden para visualizacion tomar datos en HMI
PorcentajeBomba : Real; // Porcentaje bomba
Temperatura : Real; // Temperatura
Caudal : Real; // Caudal
CaudalPermeado : Real; // Caudal permeado
Conductividad : Real; // Conductividad
Presion : Real; // Presion
SP_Caudal_100por100 : Real; // Set Point caudal 100%
SP_CaudalMinimoChequeo : Real; // Set Point caudal minimo
FechaActual {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
END_VAR
VAR_OUTPUT
EstadoChequeoActual : Bool; // Estado chequeo actual
AlarmaBajoChequeo : Bool; // Alarma bajo chequeo
VisDatoChequeo : Bool; // Visibilidad tomar datos en HMI
PorcentajeChequeoActual : Real; // Estado porcentaje actual
END_VAR
VAR RETAIN
s_PorcentajeChequeoOK { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Real := 80.0;
END_VAR
VAR
s_OrdenGuardarRegistro { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
s_OrdenBorrarRegistros { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
s_NumeroBorrado { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int; // -1= Todos / Valor= Numero de registro
s_OldGuardar { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
END_VAR
VAR RETAIN
Registro { S7_SetPoint := 'False'} : Array[1..#N_REGISTROS] of "UDT_ZC_INF_CHEQUEO";
END_VAR
VAR_TEMP
for_i : Int;
t_dummyInt : Int;
END_VAR
VAR CONSTANT
N_REGISTROS : Int := 20;
END_VAR
BEGIN
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2024 - 2026
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
El bloque `FB15050_ZC_CHEQUEO` implementa la gestión de los datos de chequeo de un sistema de membranas, manteniendo un histórico circular de hasta `#N_REGISTROS` mediciones y evaluando el rendimiento de la instalación comparando el caudal de permeado real con un valor de referencia al 100% de la bomba.
Funcionalidades principales:
- Detección de flanco ascendente en la orden de guardado: `#s_OrdenGuardarRegistro` se activa con un pulso de `#GuardarRegistro` mediante la variable estática `#s_P_Trig_Guardar`, evitando registros duplicados por nivel alto mantenido.
- Adquisición de datos: al recibir la orden de guardado, se desplazan los registros existentes una posición hacia abajo mediante `MOVE_BLK_VARIANT` y se almacena en `#Registro[1]` el conjunto de variables de proceso (`PorcentajeBomba`, `Temperatura`, `Caudal`, `CaudalPermeado`, `Conductividad`, `Presion`), junto con el `ResultadoChequeo` calculado como el porcentaje de caudal de permeado frente a `SP_Caudal_100por100`, y la fecha actual convertida con `FC15005_ZC_DTL_TO_ZC_FECHA` a la estructura `ZC_FECHA`.
- Cálculo del porcentaje de chequeo en tiempo real: `PorcentajeChequeoActual` se obtiene como el cociente entre el `CaudalPermeado` actual y `SP_Caudal_100por100`, y `EstadoChequeoActual` se activa cuando dicho porcentaje alcanza o supera `#s_PorcentajeChequeoOK`.
- Alarma de bajo chequeo: se activa `#AlarmaBajoChequeo` cuando `#ComprobarBajoChequeo` está habilitado y el `Caudal` cae por debajo de `#SP_CaudalMinimoChequeo`. Se desactiva con `#ResetBajoChequeo`.
- Animación de resultados: para cada registro se evalúa `ResultadoAnimacion`, que se activa cuando el `ResultadoChequeo` del registro es igual o superior a 80.0%, permitiendo al HMI colorear el histórico de forma intuitiva.
- Visibilidad de toma de dato en HMI: `VisDatoChequeo` refleja el estado de `#TomarDatoChequeo` para señalizar al operador que la fase de toma de datos está activa.
- Borrado de registros selectivo: según el valor de `#s_NumeroBorrado` se distingue entre borrado total (`0` o `-1`), borrado de un registro intermedio (con desplazamiento hacia abajo para mantener la trazabilidad) y borrado del último registro. En todos los casos se inicializan a cero todos los campos del registro (analógicos y de fecha).
> **Restricciones:** Ninguna.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15005_ZC_DTL_TO_ZC_FECHA` |
| FB | - |
| DB | - |
| UDT | `ZC_FECHA`, `ZC_CHEQUEO` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 18.07.2023 | (ABH) | Primera versión. |
| 01.00.01 | 21.07.2023 | (ABH) | Se eliminan entradas `Etapa actual` y `tiempo restante`. La gestión de guardado y comprobación de bajo chequeo se realiza mediante entradas BOOL. |
| 01.00.02 | 11.10.2023 | (HCR) | Se añaden entradas `TomarDatoChequeo` y salida `VisDatoChequeo` para mostrar en HMI la toma de datos por parte del operador. |
| 01.00.02 | 03.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION ORDEN_GUARDAR
// Orden guardar registro
#s_OrdenGuardarRegistro := #GuardarRegistro AND NOT #s_OldGuardar;
// Estado anterior
#s_OldGuardar := #GuardarRegistro;
END_REGION ORDEN_GUARDAR
// ==========================================================================================================
REGION ADQUISICION_DE_DATOS
// Con flanco de guardar datos, desplazamos los registros y almacenamos los datos del chequeo en la poisicion 1
IF #s_OrdenGuardarRegistro THEN
// Desplazamiento de datos
#t_dummyInt := MOVE_BLK_VARIANT(SRC := #Registro, COUNT := INT_TO_UDINT(#N_REGISTROS - 1), SRC_INDEX := 0, DEST_INDEX := 1, DEST => #Registro);
// Adquisicion de datos
#Registro[1].PorcentajeBomba := #PorcentajeBomba;
#Registro[1].Temperatura := #Temperatura;
#Registro[1].Caudal := #Caudal;
#Registro[1].CaudalPermeado := #CaudalPermeado;
#Registro[1].Conductividad := #Conductividad;
#Registro[1].Presion := #Presion;
#Registro[1].ResultadoChequeo := #CaudalPermeado / #SP_Caudal_100por100 * 100.0;
#Registro[1].Fecha := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaActual);
END_IF;
END_REGION ADQUISICION_DE_DATOS
// ==========================================================================================================
REGION CALCULO_%_CHEQUEO
// Calculo porcentaje del chequeo
IF #SP_Caudal_100por100 > 0.0 THEN
#PorcentajeChequeoActual := (#Registro[1].CaudalPermeado / #SP_Caudal_100por100) * 100.0;
ELSE
#PorcentajeChequeoActual := 0.0;
END_IF;
// Estado del chequeo actual
#EstadoChequeoActual := #PorcentajeChequeoActual >= #s_PorcentajeChequeoOK;
END_REGION CALCULO_%_CHEQUEO
// ==========================================================================================================
REGION VISIBILIDADES
// Visibilidad bajo chequeo
IF #ComprobarBajoChequeo AND (#Caudal < #SP_CaudalMinimoChequeo) THEN
#AlarmaBajoChequeo := TRUE;
END_IF;
IF #ResetBajoChequeo THEN
#AlarmaBajoChequeo := FALSE;
END_IF;
// Visibilidad Resultado Chequeo
FOR #for_i := 1 TO #N_REGISTROS DO
#Registro[#for_i].ResultadoAnimacion := (#Registro[#for_i].ResultadoChequeo >= 80.0);
END_FOR;
// Visibilidad tomar dato chequeo en HMI
#VisDatoChequeo := #TomarDatoChequeo;
END_REGION VISIBILIDADES
// ==========================================================================================================
REGION BORRADO
// Borrado de todos los registros
IF #s_OrdenBorrarRegistros THEN
IF #s_NumeroBorrado = -1 OR #s_NumeroBorrado = 0 THEN
// ==============================================================================
// Borrado total
FOR #for_i := 1 TO #N_REGISTROS DO
#Registro[#for_i].PorcentajeBomba := 0.0;
#Registro[#for_i].Temperatura := 0.0;
#Registro[#for_i].Caudal := 0.0;
#Registro[#for_i].CaudalPermeado := 0.0;
#Registro[#for_i].Conductividad := 0.0;
#Registro[#for_i].Presion := 0.0;
#Registro[#for_i].ResultadoChequeo := 0.0;
#Registro[#for_i].Fecha.Ano := 0;
#Registro[#for_i].Fecha.Mes := 0;
#Registro[#for_i].Fecha.Dia := 0;
#Registro[#for_i].Fecha.DiaSemana := 0;
#Registro[#for_i].Fecha.Hora := 0;
#Registro[#for_i].Fecha.Minuto := 0;
#Registro[#for_i].Fecha.Segundo := 0;
#Registro[#for_i].Fecha.NanoSegundo := 0;
END_FOR;
ELSIF #s_NumeroBorrado >= 1 AND #s_NumeroBorrado < #N_REGISTROS THEN
// ==============================================================================
// Borrado registro especifico
#Registro[#s_NumeroBorrado].PorcentajeBomba := 0.0;
#Registro[#s_NumeroBorrado].Temperatura := 0.0;
#Registro[#s_NumeroBorrado].Caudal := 0.0;
#Registro[#s_NumeroBorrado].CaudalPermeado := 0.0;
#Registro[#s_NumeroBorrado].Conductividad := 0.0;
#Registro[#s_NumeroBorrado].Presion := 0.0;
#Registro[#s_NumeroBorrado].ResultadoChequeo := 0.0;
#Registro[#s_NumeroBorrado].Fecha.Ano := 0;
#Registro[#s_NumeroBorrado].Fecha.Mes := 0;
#Registro[#s_NumeroBorrado].Fecha.Dia := 0;
#Registro[#s_NumeroBorrado].Fecha.DiaSemana := 0;
#Registro[#s_NumeroBorrado].Fecha.Hora := 0;
#Registro[#s_NumeroBorrado].Fecha.Minuto := 0;
#Registro[#s_NumeroBorrado].Fecha.Segundo := 0;
#Registro[#s_NumeroBorrado].Fecha.NanoSegundo := 0;
// ==============================================================================
// Desplazamiento de datos
#t_dummyInt := MOVE_BLK_VARIANT(SRC := #Registro,
COUNT := INT_TO_UDINT(#N_REGISTROS - #s_NumeroBorrado),
SRC_INDEX := #s_NumeroBorrado,
DEST_INDEX := #s_NumeroBorrado - 1,
DEST => #Registro);
// ==============================================================================
// Borrado del ultimo registro
#Registro[#N_REGISTROS].PorcentajeBomba := 0.0;
#Registro[#N_REGISTROS].Temperatura := 0.0;
#Registro[#N_REGISTROS].Caudal := 0.0;
#Registro[#N_REGISTROS].CaudalPermeado := 0.0;
#Registro[#N_REGISTROS].Conductividad := 0.0;
#Registro[#N_REGISTROS].Presion := 0.0;
#Registro[#N_REGISTROS].ResultadoChequeo := 0.0;
#Registro[#N_REGISTROS].Fecha.Ano := 0;
#Registro[#N_REGISTROS].Fecha.Mes := 0;
#Registro[#N_REGISTROS].Fecha.Dia := 0;
#Registro[#N_REGISTROS].Fecha.DiaSemana := 0;
#Registro[#N_REGISTROS].Fecha.Hora := 0;
#Registro[#N_REGISTROS].Fecha.Minuto := 0;
#Registro[#N_REGISTROS].Fecha.Segundo := 0;
#Registro[#N_REGISTROS].Fecha.NanoSegundo := 0;
ELSIF #s_NumeroBorrado = #N_REGISTROS THEN
// ==============================================================================
// Borrado de ultimo registro
#Registro[#s_NumeroBorrado].PorcentajeBomba := 0.0;
#Registro[#s_NumeroBorrado].Temperatura := 0.0;
#Registro[#s_NumeroBorrado].Caudal := 0.0;
#Registro[#s_NumeroBorrado].CaudalPermeado := 0.0;
#Registro[#s_NumeroBorrado].Conductividad := 0.0;
#Registro[#s_NumeroBorrado].Presion := 0.0;
#Registro[#s_NumeroBorrado].ResultadoChequeo := 0.0;
#Registro[#s_NumeroBorrado].Fecha.Ano := 0;
#Registro[#s_NumeroBorrado].Fecha.Mes := 0;
#Registro[#s_NumeroBorrado].Fecha.Dia := 0;
#Registro[#s_NumeroBorrado].Fecha.DiaSemana := 0;
#Registro[#s_NumeroBorrado].Fecha.Hora := 0;
#Registro[#s_NumeroBorrado].Fecha.Minuto := 0;
#Registro[#s_NumeroBorrado].Fecha.Segundo := 0;
#Registro[#s_NumeroBorrado].Fecha.NanoSegundo := 0;
END_IF;
#s_OrdenBorrarRegistros := FALSE;
END_IF;
END_REGION BORRADO
END_FUNCTION_BLOCK
@@ -0,0 +1,270 @@
FUNCTION_BLOCK "FB15055_ZC_HORAS_MAQUINA"
TITLE = FB15055_ZC_HORAS_MAQUINA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Bloque de funcion para la gestion de horas de funcionamiento
VAR_INPUT
Pulso1Seg : Bool; // Pulso 1 Segundo
FechaHora {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
IniciarTiempoMaquina : Bool; // Orden Contar tiempo maquina
IniciarTiempoProd : Bool; // Orden contar tiempo produccion
IniciarTiempoRecirc : Bool; // Orden contar tiempo recirculacion
IniciarTiempoEnzim : Bool; // Orden contar tiempo planta con enzimatico
IniciarTiempoCIP : Bool;
IniciarTiempoPausa : Bool;
ResetTiempoEnzim : Bool; // Orden reset tiempo planta con enzimatico
END_VAR
VAR_INPUT RETAIN
HabilitarMant : Bool; // Habilitar gestion mantenimiento
SP_HorasAlarma : UDInt; // Numero de Horas para alarmas
SP_MinutosAlarma : USInt; // Numero de Minutos para alarmas
END_VAR
VAR_INPUT
ProcesoID : Int; // ID Proceso para trazabilidad
END_VAR
VAR_INPUT RETAIN
Usuario : String[25]; // Usuario actual
END_VAR
VAR_OUTPUT
ParoMantenimiento : Bool; // Paro maquina por horas de mantenimiento
VisTiempoRecirculacion : Bool; // Visibilidad tiempo recirculacion
END_VAR
VAR RETAIN
TiempoMaquina { S7_SetPoint := 'False'} : "UDT_ZC_CONTADOR_TIEMPO"; // Horas totales de la maquina
TiempoMantenimiento { S7_SetPoint := 'False'} : "UDT_ZC_CONTADOR_TIEMPO"; // Horas totales mantenimiento
TiempoProduccionActual : "UDT_ZC_CONTADOR_TIEMPO";
TiempoProduccionTotal : "UDT_ZC_CONTADOR_TIEMPO";
TiempoRecirculacion : "UDT_ZC_CONTADOR_TIEMPO";
TiempoEnzimatica : "UDT_ZC_CONTADOR_TIEMPO";
TiempoLimpieza : "UDT_ZC_CONTADOR_TIEMPO";
TiempoPausa : "UDT_ZC_CONTADOR_TIEMPO";
END_VAR
VAR
Pulsador : "UDT_ZC_PULSADOR"; // Pulsador reset mantenimiento
PasswordHMI : UInt; // Contraseña HMI
s_OldAvisoHoras { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // Pulso alarma horas
s_OldInicioProduccion : Bool; // Pulso inicio produccion
s_OldInicioCIP : Bool;
s_OldInicioPausa : Bool;
s_OldCondicionParo { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
s_PasswordCalculado { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt; // Contraseña calculada de mantenimiento
END_VAR
VAR_TEMP
t_OrdenReset : Bool; // Orden Reset contador de horas
t_CondicionParo : Bool;
t_ResetProdActual : Bool;
t_ResetLimpieza : Bool;
t_ResetPausa : 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
El bloque `FB15055_ZC_HORAS_MAQUINA` implementa la gestión integral de las horas de funcionamiento de la máquina, llevando el conteo de tiempo acumulado de operación, tiempos de producción (actual y total), mantenimiento, recirculación, limpieza, pausa y tiempo de planta parada con enzimático. Incluye además la gestión del pulsador de reset de mantenimiento protegido por contraseña HMI y la gestión de los avisos por horas alcanzadas.
Funcionalidades principales:
- Conteo de horas de funcionamiento de la máquina (`TiempoMaquina`) mediante el bloque `FC15025_ZC_CONTADOR_TIEMPO` con estructura `UDT_ZC_CONTADOR_TIEMPO`.
- Conteo diferenciado de tiempo de producción actual (`TiempoProduccionActual`), producción total (`TiempoProduccionTotal`), tiempo de mantenimiento (`TiempoMantenimiento`), tiempo de recirculación (`TiempoRecirculacion`), tiempo de enzimática (`TiempoEnzimatica`), tiempo de limpieza (`TiempoLimpieza`) y tiempo de pausa (`TiempoPausa`), todos basados en `UDT_ZC_CONTADOR_TIEMPO`.
- Gestión del pulsador de reset de mantenimiento (`Pulsador` tipo `UDT_ZC_PULSADOR`) con cálculo y verificación de contraseña HMI (`PasswordHMI` / `PasswordCalculado`).
- Gestión de los flancos de inicio de producción, inicio de CIP e inicio de pausa mediante variables estáticas `s_P_Trig_*` para detección de pulsos de activación.
- Activación de la alarma y el paro de mantenimiento (`OutputParoMantenimiento`) por horas acumuladas, con flag de aviso (`s_Trig_AvisoHoras`).
- Señales de visibilidad HMI para los distintos tiempos (`VisTiempoRecirculacion`, etc.) y para el paro de mantenimiento.
- Orden interna de reset de tiempos (`t_OrdenReset`) que se activa tras la pulsación autorizada del reset de mantenimiento, reinicializando los contadores pertinentes.
> **Restricciones:** Ninguna.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15025_ZC_CONTADOR_TIEMPO` |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_CONTADOR_TIEMPO`, `UDT_ZC_PULSADOR` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 28.07.2023 | (HCR/ABH) | Primera versión. Cambio en lógica funcionamiento del bloque. Además de la gestión de alarma mantenimiento, se añade gestión de horas de funcionamiento totales de la máquina. Se añade tiempo de planta parada con enzimático de la máquina. |
| 01.00.01 | 04.03.2024 | (ABH) | Se corrige gestión de tiempo total de producción. |
| 01.00.02 | 23.05.2024 | (HCR) | Se añade salida visibilidad para tiempo de recirculación. |
| 01.00.03 | 29.10.2025 | (ABH) | Se corrige gestión de reset tiempo enzimática. |
| 01.00.04 | 03.12.2025 | (ABH) | Se añade tiempo de limpieza y tiempo de pausa. |
| 01.01.00 | 03.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION PULSADORES
// Pulsador comprobacion contraseña
"FC15026_ZC_PULSADOR"(Pulso1Seg:=#Pulso1Seg,
SP_Tiempo:=2,
TrazaCategoria:="TRZ_CAT_4_PRO_PUL",
TrazaIdProceso:=#ProcesoID,
TrazaIdPulsador:="TRZ_SYS_PUL_HOR_MAQ_RST",
Usuario:=#Usuario,
FechaHoraActual:=#FechaHora,
Pulsador:=#Pulsador);
END_REGION PULSADORES
// ==========================================================================================================
REGION PARO_MANTENIMIENTO
// Condicion de paro por mantenimiento
#t_CondicionParo :=
#HabilitarMant AND
(#TiempoMantenimiento.Hora = #SP_HorasAlarma) AND
(#FechaHora.MINUTE = #SP_MinutosAlarma);
// Con paro por mantenimiento, activamos la marca de paro
IF #t_CondicionParo AND NOT #s_OldCondicionParo THEN
#s_PasswordCalculado := 0;
#ParoMantenimiento := TRUE;
END_IF;
// Estado anterior condicion paro por mantenimiento
#s_OldCondicionParo := #t_CondicionParo;
// Ligica de paro y calculo de contraseña
IF #ParoMantenimiento THEN
// Calculo de contraseña
#s_PasswordCalculado := #FechaHora.YEAR + #FechaHora.MONTH + #FechaHora.DAY;
#Pulsador.Permiso := TRUE;
#Pulsador.Visibilidad := TRUE;
// Comprobacion de contraseñas desde HMI
IF #Pulsador.Plc THEN
// Contraseña correcta, reseteamos todo
IF #s_PasswordCalculado = #PasswordHMI THEN
#s_PasswordCalculado := 0;
#PasswordHMI := 0;
#ParoMantenimiento := FALSE;
#t_OrdenReset := TRUE;
ELSE
// Contraseña incorrecta
#PasswordHMI := 0;
#t_OrdenReset := FALSE;
END_IF;
ELSE
#t_OrdenReset := FALSE;
END_IF;
ELSE
// Reset de estados si no tenemos la señal de paro por mantenimiento
#Pulsador.Permiso := FALSE;
#Pulsador.Visibilidad := FALSE;
#t_OrdenReset := FALSE;
END_IF;
// Si no esta habilitado el paro por mantenimiento, reseteamos
IF NOT #HabilitarMant THEN
#ParoMantenimiento := FALSE;
END_IF;
END_REGION PARO_MANTENIMIENTO
// ==========================================================================================================
REGION CONTADORES
// Resolución de flancos en memoria temporal para Clean Code
#t_ResetProdActual := #IniciarTiempoProd AND NOT #s_OldInicioProduccion;
#s_OldInicioProduccion := #IniciarTiempoProd;
#t_ResetLimpieza := #IniciarTiempoCIP AND NOT #s_OldInicioCIP;
#s_OldInicioCIP := #IniciarTiempoCIP;
#t_ResetPausa := #IniciarTiempoPausa AND NOT #s_OldInicioPausa;
#s_OldInicioPausa := #IniciarTiempoPausa;
// ==========================================================================================================
// Contador de tiempo de mantenimiento
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg:=#Pulso1Seg,
Iniciar:=#HabilitarMant AND #IniciarTiempoMaquina,
Reset:=#t_OrdenReset OR NOT #HabilitarMant,
Contador:=#TiempoMantenimiento);
// ==========================================================================================================
// Contador de tiempo total Maquina
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
Iniciar := #IniciarTiempoMaquina,
Reset := FALSE,
Contador := #TiempoMaquina);
// ==========================================================================================================
// Contador de tiempo de produccion actual
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
Iniciar := #IniciarTiempoProd,
Reset := #t_ResetProdActual,
Contador := #TiempoProduccionActual);
// ==========================================================================================================
// Contador de tiempo de produccion total
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
Iniciar := #IniciarTiempoProd,
Reset := FALSE,
Contador := #TiempoProduccionTotal);
// ==========================================================================================================
// Contador de tiempo de recirculacion
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
Iniciar := #IniciarTiempoRecirc,
Reset := NOT #IniciarTiempoRecirc,
Contador := #TiempoRecirculacion);
// Visibilidad tiempo recirculacion
#VisTiempoRecirculacion := #IniciarTiempoRecirc;
// ==========================================================================================================
// Contador de tiempo de planta con enzimatico
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
Iniciar := #IniciarTiempoEnzim,
Reset := #ResetTiempoEnzim,
Contador := #TiempoEnzimatica);
// ==========================================================================================================
// Contador de tiempo de limpieza
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
Iniciar := #IniciarTiempoCIP,
Reset := #t_ResetLimpieza,
Contador := #TiempoLimpieza);
// ==========================================================================================================
// Contador de tiempo en pausa
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
Iniciar := #IniciarTiempoPausa,
Reset := #t_ResetPausa,
Contador := #TiempoPausa);
END_REGION CONTADORES
END_FUNCTION_BLOCK
@@ -0,0 +1,277 @@
FUNCTION_BLOCK "FB15056_ZC_SIVE_MEMBRANA_CARE"
TITLE = FB15056_ZC_REGISTRO_MEMBRANAS
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : HCR
FAMILY : ZeusControl
VERSION : 1.0
//Bloque de funcion para la gestion del registro de membranas
VAR_INPUT
Pulso1Seg : Bool; // Pulso 1 Segundo
OrdenRegistroGeneral : Bool; // OrdenRegistroGeneral
FechaHora {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
Iniciar : Bool; // Orden contador de tiempo
END_VAR
VAR_INPUT RETAIN
Usuario : String[25]; // Usuario
END_VAR
VAR_INPUT
TecnicoRegistroGeneral : String[20]; // Tecnico registro general
END_VAR
VAR_INPUT RETAIN
TiempoAvisoOn : Int; // Tiempo indicación aviso On
TiempoAvisoOff : Int; // Tiempo indicación aviso Off
TiempoAlarmaOn : Int; // Tiempo indicación alarma On
TiempoAlarmaOff : Int; // Tiempo indicación alarma Off
HorasAviso : Int; // Horas para aviso
HorasAlarma : Int; // Horas para alarma
HorasAlarmaCritica : Int; // Horas para alarma critica
END_VAR
VAR_OUTPUT
Aviso : Bool; // Estado de aviso
Alarma : Bool; // Estado de alarma
AlarmaCritica : Bool; // Estado de alarma critica
Estado : Int; // Estado
TiempoMembranaRestante : UDInt; // Tiempo membrana restante
END_VAR
VAR_IN_OUT
OrdenBorrar : Bool; // Ordrn borrar registros
END_VAR
VAR RETAIN
Registros { S7_SetPoint := 'False'} : Array[1..#N_REGISTROS] of "UDT_ZC_SIVE_MEMBRANA_CARE";
END_VAR
VAR
Pulsador { S7_SetPoint := 'False'} : "UDT_ZC_PULSADOR"; // Pulsador nuevo registro
END_VAR
VAR RETAIN
Tiempo { S7_SetPoint := 'False'} : "UDT_ZC_CONTADOR_TIEMPO"; // Tiempo actual membranas
s_PuestaMarchaAno : UInt; // Fecha de puesta en marcha
s_PuestaMarchaMes : Int; // Fecha de puesta en marcha
s_PuestaMarchaDia : Int; // Fecha de puesta en marcha
s_Indice : Int := 1; // Indice registro
s_TecnicoNuevoRegistro : String[20]; // Nombre tecnico nuevo registro
s_FlipFlopAviso { S7_SetPoint := 'False'} : "UDT_ZC_DISP_FF";
s_FlipFlopAlarma { S7_SetPoint := 'False'} : "UDT_ZC_DISP_FF";
END_VAR
VAR_TEMP
for_i : Int; // Bucle para recorrer los registros
t_OrdenReset : Bool; // Orden Reset contador de horas
END_VAR
VAR CONSTANT
N_REGISTROS : Int := 10; // Número de registros
END_VAR
BEGIN
REGION DESCRIPCION
//=======================================================================
// (C)Copyright ZeusControl 2023 - 2026
//=======================================================================
(*
### Información del Sistema
| Hardware Compatible | Entorno de Ingeniería |
|---------------------|-----------------------|
| PLC serie 1200/1500 | TIA Portal 18 |
---
### Descripción Funcional
El bloque `FB15056_ZC_REGISTRO_MEMBRANAS` implementa la gestión integral del registro y vida útil de las membranas. Mantiene un histórico circular de intervenciones (`Registros[i]`), controla el tiempo de uso acumulado y genera los avisos, alarmas y alarma crítica en función de las horas restantes, incluyendo la posibilidad de registrar cambios generales de forma masiva.
Funcionalidades principales:
- Registro de intervención por pulsador: el bloque gestiona un pulsador (`FC15026_ZC_PULSADOR`) con permiso dependiente de que haya un técnico introducido en `#s_TecnicoNuevoRegistro`. Al confirmarse la pulsación (`Pulsador.Plc`), se almacena en `#Registros[#s_Indice]` la fecha/hora convertida con `FC15005_ZC_DTL_TO_ZC_FECHA`, el técnico y el tiempo de membrana (`#Tiempo.Hora`), se limpia el técnico y se dispara la orden interna `#t_OrdenReset` para reiniciar el contador de tiempo. El índice avanza de forma circular hasta `#N_REGISTROS`.
- Registro general: la entrada `#OrdenRegistroGeneral` permite registrar un cambio de membrana sin pulsador físico, asociando el técnico definido en `#TecnicoRegistroGeneral` y siguiendo la misma lógica circular de índice.
- Contador de tiempo de membrana: mediante `FC15025_ZC_CONTADOR_TIEMPO` se acumula el tiempo de uso en horas, con posibilidad de reset a través de `#t_OrdenReset`.
- Cálculo de horas restantes: `#TiempoMembranaRestante` se calcula como la diferencia entre `#HorasAlarmaCritica` y `#Tiempo.Hora`, acotado a `0` para evitar desbordamientos cuando se supera el umbral crítico.
- Borrado de registros: la entrada `#OrdenBorrar` limpia completamente el array de registros (fecha, hora, técnico y tiempo de membrana) y reinicia el índice de escritura a `1`.
- Estado global por tramos: clasificación del estado en `#Estado` (0: Normal, 1: Aviso, 2: Alarma, 3: Alarma crítica) según el rango de horas acumulado frente a `#HorasAviso`, `#HorasAlarma` y `#HorasAlarmaCritica`.
- Aviso y Alarma intermitentes: se generan mediante dos Flip-Flop (`FC2926_ZC_FF_INDV`) con tiempos de encendido/apagado configurables (`#TiempoAvisoOn/Off`, `#TiempoAlarmaOn/Off`) para producir señales parpadeantes. La alarma crítica (`AlarmaCritica`) es continua y se activa definitivamente al alcanzar `#HorasAlarmaCritica`.
> **Restricciones:** Ninguna.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15026_ZC_PULSADOR`, `FC15025_ZC_CONTADOR_TIEMPO`, `FC2926_ZC_FF_INDV`, `FC15005_ZC_DTL_TO_ZC_FECHA` |
| FB | - |
| DB | - |
| UDT | `ZC_FECHA`, `UDT_ZC_PULSADOR`, `UDT_ZC_CONTADOR_TIEMPO`. `UDT_ZC_SIVE_MEMBRANA_CARE` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 10.05.2023 | (HCR) | Primera versión. |
| 01.00.01 | 02.10.2023 | (ABH) | Cambio nombre a SIVE MEMBRANA CARE. Se corrigen permisos variables estáticas. Se corrigen nombres de variables. Se crea UDT `registro membranas`. Se cambia tipo de dato fecha/hora string a `ZC_FECHA`. Se quitan temporizadores y se utiliza Flip-Flop individual. Se eliminan variables innecesarias. |
| 01.00.02 | 16.01.2024 | (ABH) | Se limita el tiempo restante de las membranas para que no se desborde. |
| 01.00.03 | 02.12.2025 | (ABH) | Se corrige cálculo tiempo horas restantes. |
| 01.00.04 | 10.01.2026 | (CNR) | Se añade orden y gestión de registro general. |
*)
END_REGION DESCRIPCION
// =============================================================================
REGION ORDEN_REGISTRO
#Pulsador.Permiso := (#s_TecnicoNuevoRegistro <> '');
"FC15026_ZC_PULSADOR"(Pulso1Seg := #Pulso1Seg,
SP_Tiempo := 2,
TrazaCategoria:=0,
TrazaIdProceso:=0,
TrazaIdPulsador:=0,
Usuario := #Usuario,
FechaHoraActual := #FechaHora,
Pulsador := #Pulsador);
// Orden pulsador
IF #Pulsador.Plc THEN
#Registros[#s_Indice].FechaHora := "FC15005_ZC_DTL_TO_ZC_FECHA"( #FechaHora);
#Registros[#s_Indice].Tecnico := #s_TecnicoNuevoRegistro;
#Registros[#s_Indice].TiempoMembrana := #Tiempo.Hora;
// Reset valor técnico y orden reset tiempo
#s_TecnicoNuevoRegistro := '';
#t_OrdenReset := TRUE;
// Incrementamos indice y si es mayor que el número máximo de registro lo ponemos en la posición inicial
#s_Indice += 1;
IF #s_Indice > #N_REGISTROS THEN
#s_Indice := 1;
END_IF;
END_IF;
// Orden registro general
IF #OrdenRegistroGeneral THEN
#Registros[#s_Indice].FechaHora := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaHora);
#Registros[#s_Indice].Tecnico := #TecnicoRegistroGeneral;
#Registros[#s_Indice].TiempoMembrana := #Tiempo.Hora;
// Reset valor técnico y orden reset tiempo
#t_OrdenReset := TRUE;
// Incrementamos indice y si es mayor que el número máximo de registro lo ponemos en la posición inicial
#s_Indice += 1;
IF #s_Indice > #N_REGISTROS THEN
#s_Indice := 1;
END_IF;
END_IF;
END_REGION ORDEN_REGISTRO
// =============================================================================
REGION TIEMPO_MEMBRANAS
// Contador de tiempo
"FC15025_ZC_CONTADOR_TIEMPO"(Pulso1seg := #Pulso1Seg,
Iniciar := #Iniciar,
Contador := #Tiempo,
Reset := #t_OrdenReset);
// Horas Restante
IF INT_TO_UDINT(#HorasAlarmaCritica) > #Tiempo.Hora THEN
#TiempoMembranaRestante := INT_TO_UDINT(#HorasAlarmaCritica) - #Tiempo.Hora;
ELSE
#TiempoMembranaRestante := 0;
END_IF;
END_REGION TIEMPO_MEMBRANAS
// =============================================================================
REGION BORRAR_REGISTROS
IF #OrdenBorrar THEN
FOR #for_i := 1 TO #N_REGISTROS DO
#Registros[#for_i].FechaHora.Ano := 0;
#Registros[#for_i].FechaHora.Mes := 0;
#Registros[#for_i].FechaHora.Dia := 0;
#Registros[#for_i].FechaHora.Hora := 0;
#Registros[#for_i].FechaHora.Minuto := 0;
#Registros[#for_i].FechaHora.Segundo := 0;
#Registros[#for_i].FechaHora.NanoSegundo := 0;
#Registros[#for_i].Tecnico := '';
#Registros[#for_i].TiempoMembrana := 0;
#s_Indice := 1;
END_FOR;
#OrdenBorrar := FALSE;
END_IF;
END_REGION BORRAR_REGISTROS
// =============================================================================
REGION ESTADO
#s_FlipFlopAviso.Orden_Activar := FALSE;
#s_FlipFlopAviso.Config_SetPointTiempoFlip := #TiempoAvisoOn;
#s_FlipFlopAviso.Config_SetPointTiempoFlop := #TiempoAvisoOff;
#s_FlipFlopAlarma.Orden_Activar := FALSE;
#s_FlipFlopAlarma.Config_SetPointTiempoFlip := #TiempoAlarmaOn;
#s_FlipFlopAlarma.Config_SetPointTiempoFlop := #TiempoAlarmaOff;
// Estado Aviso
#Estado := 0;
IF #Tiempo.Hora >= #HorasAviso AND #Tiempo.Hora < #HorasAlarma THEN
#s_FlipFlopAviso.Orden_Activar := TRUE;
#Aviso := #s_FlipFlopAviso.#Estado_Flip;
#Estado := 1;
ELSE
#Aviso := FALSE;
END_IF;
// Estado Alarma
IF #Tiempo.Hora >= #HorasAlarma AND #Tiempo.Hora < #HorasAlarmaCritica THEN
#s_FlipFlopAlarma.Orden_Activar := TRUE;
#Alarma := #s_FlipFlopAlarma.#Estado_Flip;
#Estado := 2;
ELSE
#Alarma := FALSE;
END_IF;
// Estado Alarma Crítica
IF #Tiempo.Hora >= #HorasAlarmaCritica THEN
#AlarmaCritica := TRUE;
#Estado := 3;
ELSE
#AlarmaCritica := FALSE;
END_IF;
END_REGION ESTADO
// =============================================================================
REGION GESTION_FF
// =============================================================================
// Flip Flop aviso
"FC2926_ZC_FF_INDV"(Pulso1Seg := #Pulso1Seg,
FF := #s_FlipFlopAviso);
// =============================================================================
// Flip Flop alarma
"FC2926_ZC_FF_INDV"(Pulso1Seg := #Pulso1Seg,
FF := #s_FlipFlopAlarma);
END_REGION GESTION_FF
END_FUNCTION_BLOCK
@@ -0,0 +1,261 @@
FUNCTION_BLOCK "FB15057_ZC_CONT_PROD_MEM"
TITLE = FB15057_ZC_CONT_PROD_MEM
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Bloque de funcion para gestion de contadores de produccion maquinas membranas
VAR_INPUT
InicioProduccion : Bool; // En etapa inicio produccion
EnProduccion : Bool; // En etapas de produccion
FinProduccion : Bool; // En etapa fin de produccion
FechaHoraActual {InstructionName := 'DTL'; LibVersion := '1.0'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : DTL; // Fecha y hora actual
LitrosEntrada : LReal; // Totalizador litros entrada
LitrosConcentrado : LReal; // Totalizador litros concentrado
LitrosPermeado : LReal; // Totalizador litros permeado
FCV : Real; // Factor de concentradion
Brix : LReal; // Media de grados BRIX
TempAlimentacion : LReal; // Temperatura alimentacion
TempConcentrado : LReal; // Temperatura Concentrado
TempPermeado : LReal; // Temperatura Permeado
BorrarParcial : Bool; // Borrar datos parciales
END_VAR
VAR RETAIN
Ultima { S7_SetPoint := 'False'} : "UDT_ZC_CONT_PROD_MEM";
Penultima { S7_SetPoint := 'False'} : "UDT_ZC_CONT_PROD_MEM";
Parcial { S7_SetPoint := 'False'} : "UDT_ZC_CONT_PROD_MEM";
Total { S7_SetPoint := 'False'} : "UDT_ZC_CONT_PROD_MEM";
END_VAR
VAR
s_OldInicioProd { S7_SetPoint := 'True'} : Bool;
s_OldFinProd { S7_SetPoint := 'True'} : Bool;
s_InicioProduccion : Bool; // Orden inicio produccion
s_FinProduccion : Bool; // Orden fin de produccion
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 funcion que implementa la gestión de los contadores y totalizadores de producción de una máquina de membranas, manteniendo la trazabilidad histórica de la última y penúltima producción, así como los acumulados parciales y totales, junto con el cálculo del factor de conversión volumétrica (FCV) asociado a cada periodo.
Funcionalidades principales:
- Detección de flanco ascendente en las órdenes `#InicioProduccion` y `#FinProduccion` mediante las variables estáticas `#s_P_Trig_InicioProd` y `#s_P_Trig_FinProd`, generando los pulsos internos `#s_InicioProduccion` y `#s_FinProduccion` para evitar disparos múltiples por nivel mantenido.
- Inicio de producción: al recibir `#s_InicioProduccion` se trasladan los datos de la última producción a la estructura `#Penultima`, se inicializan a cero los campos analíticos y de fecha de `#Ultima`, y se registra la fecha/hora de inicio en `#Ultima.FechaInicio` mediante la conversión realizada por `FC15005_ZC_DTL_TO_ZC_FECHA`.
- Datos en curso: mientras `#EnProduccion` está activo, se copian en `#Ultima` las variables de proceso (`LitrosEntrada`, `LitrosConcentrado`, `Brix`, `TempAlimentacion`, `TempConcentrado`, `TempPermeado`), manteniendo actualizada la instantánea de la producción en marcha.
- Fin de producción: al detectar `#s_FinProduccion` se registra la fecha/hora de fin en `#Ultima.FechaFin` y se acumulan los valores de la producción actual sobre los contadores `#Parcial` y `#Total`, tanto de litros de entrada como de concentrado.
- Cálculo de litros de permeado por diferencia (`LitrosEntrada - LitrosConcentrado`) para las cuatro estructuras: `#Ultima`, `#Penultima`, `#Parcial` y `#Total`.
- Cálculo del factor de conversión volumétrica (FCV) para cada estructura con protección frente a división por cero (cuando `#LitrosEntrada` o `#LitrosConcentrado` son menores o iguales a `0.0`, el resultado se fija en `0.0`).
- Reset de la producción parcial: la entrada `#BorrarParcial` reinicia los litros del contador parcial y actualiza `#Parcial.FechaInicio` con la fecha/hora actual.
> **Restricciones:** Ninguna.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15005_ZC_DTL_TO_ZC_FECHA` |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_CONTADORES_PRODUCCION` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 01.00.00 | 29.09.2023 | (ABH) | Primera versión. |
| 01.00.01 | 04.03.2024 | (ABH) | Se corrige gestión de marca fin de producción. |
| 01.00.02 | 03.07.2026 | (ABH) | Cambio a SCL. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_ORDENES
// Inicio de produccion
#s_InicioProduccion := #InicioProduccion AND NOT #s_OldInicioProd;
// Fin de produccion
#s_FinProduccion := #FinProduccion AND NOT #s_OldFinProd;
// Estados anteriores
#s_OldInicioProd := #InicioProduccion;
#s_OldFinProd := #FinProduccion;
END_REGION GESTION_ORDENES
// ==========================================================================================================
REGION MOVIMIENTO_DE_DATOS
// ==========================================================================================================
REGION INICIO_PRODUCCION
IF #s_InicioProduccion THEN
// =================================================================================================
// Traspaso de datos de ultima produccion a penultima produccion
#Penultima.LitrosEntrada := #Ultima.LitrosEntrada;
#Penultima.LitrosConcentrado := #Ultima.LitrosConcentrado;
#Penultima.Brix := #Ultima.Brix;
#Penultima.TempAlimentacion := #Ultima.TempAlimentacion;
#Penultima.TempConcentrado := #Ultima.TempConcentrado;
#Penultima.FechaInicio := #Ultima.FechaInicio;
#Penultima.FechaFin := #Ultima.FechaFin;
// =================================================================================================
// Reset datos ultima produccion
#Ultima.LitrosEntrada := 0.0;
#Ultima.LitrosConcentrado := 0.0;
#Ultima.Brix := 0.0;
#Ultima.TempAlimentacion := 0.0;
#Ultima.TempConcentrado := 0.0;
#Ultima.FechaInicio.Ano := 0;
#Ultima.FechaInicio.Mes := 0;
#Ultima.FechaInicio.Dia := 0;
#Ultima.FechaInicio.Hora := 0;
#Ultima.FechaInicio.Minuto := 0;
#Ultima.FechaInicio.Segundo := 0;
#Ultima.FechaFin.Ano := 0;
#Ultima.FechaFin.Mes := 0;
#Ultima.FechaFin.Dia := 0;
#Ultima.FechaFin.Hora := 0;
#Ultima.FechaFin.Minuto := 0;
#Ultima.FechaFin.Segundo := 0;
// =================================================================================================
// Guardamos la fecha y hora de inicio de la produccion
#Ultima.FechaInicio := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaHoraActual);
END_IF;
END_REGION INICIO_PRODUCCION
// ==========================================================================================================
REGION DATOS_DURANTE_PRODUCCION
// Movimientos de datos durante la produccion
IF #EnProduccion THEN
#Ultima.LitrosEntrada := #LitrosEntrada;
#Ultima.LitrosConcentrado := #LitrosConcentrado;
#Ultima.Brix := #Brix;
#Ultima.TempAlimentacion := #TempAlimentacion;
#Ultima.TempConcentrado := #TempConcentrado;
#Ultima.TempPermeado := #TempPermeado;
END_IF;
END_REGION DATOS_DURANTE_PRODUCCION
// ==========================================================================================================
REGION DATOS_AL_FIN_PRODUCCION
// Movimiento de datos al finalizar la produccion
IF #s_FinProduccion THEN
// Fecha fin
#Ultima.FechaFin := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaHoraActual);
// Suma de datos parciales y totales
#Parcial.LitrosEntrada := #Ultima.LitrosEntrada + #Parcial.LitrosEntrada;
#Parcial.LitrosConcentrado := #Ultima.LitrosConcentrado + #Parcial.LitrosConcentrado;
#Total.LitrosEntrada := #Ultima.LitrosEntrada + #Total.LitrosEntrada;
#Total.LitrosConcentrado := #Ultima.LitrosConcentrado + #Total.LitrosConcentrado;
END_IF;
END_REGION DATOS_AL_FIN_PRODUCCION
END_REGION MOVIMIENTO_DE_DATOS
// ==========================================================================================================
REGION CALCULOS
// ==========================================================================================================
REGION TOTALIZADORES
// Ultima produccion
#Ultima.LitrosPermeado := #Ultima.LitrosEntrada - #Ultima.LitrosConcentrado;
// Penultima produccion
#Penultima.LitrosPermeado := #Penultima.LitrosEntrada - #Penultima.LitrosConcentrado;
// Produccion parcial
#Parcial.LitrosPermeado := #Parcial.LitrosEntrada - #Parcial.LitrosConcentrado;
// Produccion total
#Total.LitrosPermeado := #Total.LitrosEntrada - #Total.LitrosConcentrado;
END_REGION TOTALIZADORES
// ==========================================================================================================
REGION FCV
// FCV Ultima produccion
IF #Ultima.LitrosEntrada > 0.0 AND #Ultima.LitrosConcentrado > 0.0 THEN
#Ultima.FCV := LREAL_TO_REAL(#Ultima.LitrosEntrada / #Ultima.LitrosConcentrado);
ELSE
#Ultima.FCV := 0.0;
END_IF;
// FCV Penultima produccion
IF #Penultima.LitrosEntrada > 0.0 AND #Penultima.LitrosConcentrado > 0.0 THEN
#Penultima.FCV := LREAL_TO_REAL(#Penultima.LitrosEntrada / #Penultima.LitrosConcentrado);
ELSE
#Penultima.FCV := 0.0;
END_IF;
// FCV Produccion parcial
IF #Parcial.LitrosEntrada > 0.0 AND #Parcial.LitrosConcentrado > 0.0 THEN
#Parcial.FCV := LREAL_TO_REAL(#Parcial.LitrosEntrada / #Parcial.LitrosConcentrado);
ELSE
#Parcial.FCV := 0.0;
END_IF;
// FCV Produccion Total
IF #Total.LitrosEntrada > 0.0 AND #Total.LitrosConcentrado > 0.0 THEN
#Total.FCV := LREAL_TO_REAL(#Total.LitrosEntrada / #Total.LitrosConcentrado);
ELSE
#Total.FCV := 0.0;
END_IF;
END_REGION FCV
END_REGION CALCULOS
// ==========================================================================================================
REGION RESET_DATOS
// Reset produccion parcial
IF #BorrarParcial THEN
#Parcial.LitrosEntrada := 0.0;
#Parcial.LitrosConcentrado := 0.0;
#Parcial.FechaInicio := "FC15005_ZC_DTL_TO_ZC_FECHA"(#FechaHoraActual);
END_IF;
END_REGION RESET_DATOS
END_FUNCTION_BLOCK
@@ -0,0 +1,112 @@
FUNCTION "FC15106_ZC_GESTION_LISTA" : Void
TITLE = FC15106_ZC_GESTION_LISTA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion de gestion listas
VAR_IN_OUT
Lista : Array[*] of "UDT_ZC_LISTA_ORIG_DEST";
END_VAR
VAR_TEMP
for_i : Int;
for_j : Int;
t_Index : Int;
t_LowerBound : Int;
t_UpperBound : Int;
t_YaExiste : Bool;
t_ListaTemporal : Array[0..100] of "UDT_ZC_LISTA_ORIG_DEST";
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:** Límite de listas de tipo `UDT_ZC_LISTA_ORIG_DEST` con 100 posiciones.
Función encargada de gestionar, limpiar y compactar listas de valores enteros.
- Calcular dinámicamente los límites del array de entrada `#Lista` mediante las instrucciones `LOWER_BOUND` y `UPPER_BOUND`.
- Inicializar a cero una lista temporal interna (`#t_ListaTemporal`) adaptada a la longitud del array procesado.
- Analizar cíclicamente los elementos de la `#Lista` para filtrar y omitir de forma automática aquellos que posean valor cero.
- Identificar y suprimir las entradas duplicadas para garantizar un conjunto final de datos compuesto exclusivamente por valores únicos.
- Compactar los datos filtrados y transferirlos de vuelta a la `#Lista` original, reescribiendo los valores secuencialmente desde su límite inferior.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | - |
| UDT | `UDT_ZC_LISTA_ORIG_DEST` |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 27.06.2025 | (ABH) | Primera version. |
| 01.00.00 | 03.07.2026 | (ABH) | Cambio en tipo de datos de INT a `UDT_ZC_LISTA_ORIG_DEST`. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION CALCULOS
// Obtener límites reales del array
#t_LowerBound := DINT_TO_INT(LOWER_BOUND(ARR := #Lista, DIM := 1));
#t_UpperBound := DINT_TO_INT(UPPER_BOUND(ARR := #Lista, DIM := 1));
// Inicializar lista temporal
FOR #for_i := 1 TO DINT_TO_INT((#t_UpperBound - #t_LowerBound + 1)) DO
#t_ListaTemporal[#for_i].Id := 0;
#t_ListaTemporal[#for_i].Consigna := 0;
END_FOR;
END_REGION CALCULOS
// ==========================================================================================================
REGION GESTION_LISTA
// Compactar y eliminar duplicados
#t_Index := 1;
FOR #for_i := #t_LowerBound TO #t_UpperBound DO
IF #Lista[#for_i].Id <> 0 THEN
#t_YaExiste := FALSE;
FOR #for_j := 1 TO #t_Index - 1 DO
IF #t_ListaTemporal[#for_j].Id = #Lista[#for_i].Id THEN
#t_YaExiste := TRUE;
EXIT;
END_IF;
END_FOR;
IF NOT #t_YaExiste THEN
#t_ListaTemporal[#t_Index] := #Lista[#for_i];
#t_Index := #t_Index + 1;
END_IF;
END_IF;
END_FOR;
// Copiar de vuelta a lista original
FOR #for_i := #t_LowerBound TO #t_UpperBound DO
#Lista[#for_i] := #t_ListaTemporal[#for_i - #t_LowerBound + 1];
END_FOR;
END_REGION
END_FUNCTION
@@ -0,0 +1,55 @@
DATA_BLOCK "DB20_GESTION_SCADA"
TITLE = DB16_GESTION_SCADA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
NON_RETAIN
VAR
BitDevida : Array[0.."N_MAX_SCADA"] of Bool;
ConexionOk : Array[0.."N_MAX_SCADA"] of Bool;
END_VAR
VAR RETAIN
Maestro : Int; // -1= NINGUNO / 10= SCADA01 / 11= SCADA02
END_VAR
VAR
PermisoInformes : Array[0.."N_MAX_SCADA"] of Bool;
Sincronizacion : Struct
AmbosConectados : Bool;
Informes : Array[0.."N_MAX_SCADA"] of Int;
CIP : Array[0.."N_MAX_SCADA"] of Int;
Fermentadores : Array[0.."N_MAX_SCADA"] of Int;
Cocina1 : Array[0.."N_MAX_SCADA"] of Int;
END_STRUCT;
BitVida : Array[0.."N_MAX_SCADA"] of Struct
TON_ON { S7_SetPoint := 'False'} : Array[0..1] of "UDT_ZC_DISP_TON";
TON_OFF : Array[0..1] of "UDT_ZC_DISP_TON";
END_STRUCT;
END_VAR
VAR RETAIN
TON_CIP_ESTADO : Array[0..1] of "UDT_ZC_DISP_TON";
TON_INFORME_ESTADO : Array[0..1] of "UDT_ZC_DISP_TON";
TON_FERM_ESTADO : Array[0..1] of "UDT_ZC_DISP_TON";
TON_COCINA1_ESTADO : Array[0..1] of "UDT_ZC_DISP_TON";
END_VAR
VAR
Aux : Struct
OldAmbosConectados : Bool;
END_STRUCT;
END_VAR
BEGIN
BitDevida[0] := TRUE;
ConexionOk[0] := TRUE;
Maestro := 10;
PermisoInformes[0] := TRUE;
Sincronizacion.AmbosConectados := FALSE;
Sincronizacion.Informes[0] := 0;
Sincronizacion.CIP[0] := 0;
Sincronizacion.Fermentadores[0] := 0;
Sincronizacion.Cocina1[0] := 0;
Aux.OldAmbosConectados := FALSE;
END_DATA_BLOCK
@@ -0,0 +1,288 @@
FUNCTION "FC20_GESTION_SCADA" : Void
TITLE = FC21_GESTION_SCADA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Gestor de conexiones con SCADA, para determinar quien es el maestro en un sistema de dos equipos monopuesto
VAR_TEMP
for_i : Int;
END_VAR
BEGIN
(* (*
ZEUS CONTROL, S.A.
(c)Copyright (2024) All Rights Reserved
--------------------------------------------------------------------------------------
Software: TIA Portal 18
Restricciones: PLC serie 1200/1500
Nombre: FC10_GESTION_SCADA
Descripcion: Gestor de conexiones con SCADA, para determinar quien es el maestro en un sistema de
dos equipos monopuesto
Dependencias:
FC: FC2928_ZC_TON_INDV
FC2929_ZC_TOF_INDV
FB: -
DB: -
UDT: -
Change log:
Version Fecha Tecnico a cargo Descripcion
01.00.00 30.08.2024 (ABH) Primera version.
//=====================================================================================
*)
REGION GESTION_CONEXION_OK
// ====================================================================================
// Gestion bit de vida
FOR #for_i := 1 TO "N_MAX_SCADA" DO
"DB21_GESTION_SCADA".ConexionOk[#for_i] := FALSE;
"DB21_GESTION_SCADA".TON_ON[#for_i].SP := 3;
"DB21_GESTION_SCADA".TON_OFF[#for_i].SP := 3;
"DB21_GESTION_SCADA".TON_ON[#for_i].IN := "DB21_GESTION_SCADA".BitDevida[#for_i];
"DB21_GESTION_SCADA".TON_OFF[#for_i].IN := NOT "DB21_GESTION_SCADA".BitDevida[#for_i];
IF "DB21_GESTION_SCADA".TON_ON[#for_i].Q OR "DB21_GESTION_SCADA".TON_OFF[#for_i].Q THEN
"DB21_GESTION_SCADA".ConexionOk[#for_i] := FALSE;
ELSE
"DB21_GESTION_SCADA".ConexionOk[#for_i] := TRUE;
END_IF;
END_FOR;
END_REGION
REGION DEFINICION_MAESTRO
// Si no estan las conexion OK con ninguno de los dos SCADAS, ponemos el maestro a -1
IF NOT "DB21_GESTION_SCADA".ConexionOk[0] AND NOT "DB21_GESTION_SCADA".ConexionOk[1] THEN
"DB21_GESTION_SCADA".Maestro := -1;
END_IF;
// Si todavia no se ha seleccionado ningun maestro, comprobamos el estado de la conexion con los dos Scadas.
IF "DB21_GESTION_SCADA".Maestro = -1 THEN
// Si esta OK la conexion con SCADA01, lo marcamos como el maestro actual
IF "DB21_GESTION_SCADA".ConexionOk[0] THEN
"DB21_GESTION_SCADA".Maestro := 10;
END_IF;
// Si esta OK la conexion con SCADA02 y el SCADA01 esta NO OK, lo marcamos como el maestro actual
IF NOT "DB21_GESTION_SCADA".ConexionOk[0] AND "DB21_GESTION_SCADA".ConexionOk[1] THEN
"DB21_GESTION_SCADA".Maestro := 11;
END_IF;
END_IF;
// Reset seleccion maestro si pierde la conexion
CASE "DB21_GESTION_SCADA".Maestro OF
10:
IF NOT "DB21_GESTION_SCADA".ConexionOk[0] THEN
"DB21_GESTION_SCADA".Maestro := -1;
END_IF;
11:
IF NOT "DB21_GESTION_SCADA".ConexionOk[1] THEN
"DB21_GESTION_SCADA".Maestro := -1;
END_IF;
END_CASE;
// Gestion marcas para ejecucion de scripts
"DB21_GESTION_SCADA".PermisoInformes[0] := "DB21_GESTION_SCADA".Maestro = 10;
"DB21_GESTION_SCADA".PermisoInformes[1] := "DB21_GESTION_SCADA".Maestro = 11;
END_REGION
REGION DEFICION_SINCRONIZACION
// ====================================================================================
// Comprobamos que estan ambos Scadas conectados
"DB21_GESTION_SCADA".Sincronizacion.AmbosConectados := "DB21_GESTION_SCADA".ConexionOk[0] AND "DB21_GESTION_SCADA".ConexionOk[1];
// ====================================================================================
// Reseteamos ordenes de sincronizacion.
// Si estan ambos conectados, quitamos las ordenes de sincronizacion
IF NOT "DB21_GESTION_SCADA".Sincronizacion.AmbosConectados THEN
"DB21_GESTION_SCADA".Sincronizacion.Informes[0] :=
"DB21_GESTION_SCADA".Sincronizacion.CIP[0] :=
"DB21_GESTION_SCADA".Sincronizacion.Fermentadores[0] :=
"DB21_GESTION_SCADA".Sincronizacion.Cocina1[0] := 0;
"DB21_GESTION_SCADA".Sincronizacion.Informes[1] :=
"DB21_GESTION_SCADA".Sincronizacion.CIP[1] :=
"DB21_GESTION_SCADA".Sincronizacion.Fermentadores[1] :=
"DB21_GESTION_SCADA".Sincronizacion.Cocina1[1] := 0;
END_IF;
// ====================================================================================
// Cuando se conecten los dos SCADAs, lanzamos orden para que se sincronicen
IF "DB21_GESTION_SCADA".Sincronizacion.AmbosConectados AND NOT "DB21_GESTION_SCADA".Aux.OldAmbosConectados THEN
IF "DB21_GESTION_SCADA".Maestro = 10 THEN
"DB21_GESTION_SCADA".Sincronizacion.Informes[1] :=
"DB21_GESTION_SCADA".Sincronizacion.CIP[1] :=
"DB21_GESTION_SCADA".Sincronizacion.Fermentadores[1] :=
"DB21_GESTION_SCADA".Sincronizacion.Cocina1[1] := 2;
END_IF;
IF "DB21_GESTION_SCADA".Maestro = 11 THEN
"DB21_GESTION_SCADA".Sincronizacion.Informes[0] :=
"DB21_GESTION_SCADA".Sincronizacion.CIP[0] :=
"DB21_GESTION_SCADA".Sincronizacion.Fermentadores[0] :=
"DB21_GESTION_SCADA".Sincronizacion.Cocina1[0] := 2;
END_IF;
END_IF;
"DB21_GESTION_SCADA".Aux.OldAmbosConectados := "DB21_GESTION_SCADA".Sincronizacion.AmbosConectados;
END_REGION
REGION GESTION_ESTADO_OK
// ====================================================================================
// Gestion estado sinronizacion OK
FOR #for_i := 0 TO 1 DO
"DB21_GESTION_SCADA".TON_CIP_ESTADO[#for_i].SP :=
"DB21_GESTION_SCADA".TON_INFORME_ESTADO[#for_i].SP :=
"DB21_GESTION_SCADA".TON_COCINA1_ESTADO[#for_i].SP :=
"DB21_GESTION_SCADA".TON_FERM_ESTADO[#for_i].SP := 2;
"DB21_GESTION_SCADA".TON_CIP_ESTADO[#for_i].IN := "DB21_GESTION_SCADA".Sincronizacion.CIP[#for_i] = 4 OR "DB21_GESTION_SCADA".Sincronizacion.CIP[#for_i] = 12;
IF "DB21_GESTION_SCADA".TON_CIP_ESTADO[#for_i].Q THEN
IF "DB21_GESTION_SCADA".Sincronizacion.CIP[#for_i] = 12 THEN
"FC8_ZC_TRAZA_REGISTRO"(FechaHora:="DB1_SYS".FechaHoraActual,
Registrar:=1,
Categoria:="TRAZA_CAT_1_SISTEMA",
User:='',
Codigo1:=0,
Codigo2:=0,
Codigo3:=0,
Texto:='Fallo al sincronizar registros CIP',
CodInt_1:=_int_in_,
CodInt_2:=_int_in_,
CodInt_3:=_int_in_,
CodInt_4:=_int_in_,
CodInt_5:=_int_in_,
CodReal_1:=_real_in_,
CodReal_2:=_real_in_);
END_IF;
"DB21_GESTION_SCADA".Sincronizacion.CIP[#for_i] := 0;
END_IF;
"DB21_GESTION_SCADA".TON_INFORME_ESTADO[#for_i].IN := "DB21_GESTION_SCADA".Sincronizacion.Informes[#for_i] = 4 OR "DB21_GESTION_SCADA".Sincronizacion.Informes[#for_i] = 12;
IF "DB21_GESTION_SCADA".TON_INFORME_ESTADO[#for_i].Q THEN
IF "DB21_GESTION_SCADA".Sincronizacion.Informes[#for_i] = 12 THEN
"FC8_ZC_TRAZA_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRAZA_CAT_1_SISTEMA",
User := '',
Codigo1 := 0,
Codigo2 := 0,
Codigo3 := 0,
Texto := 'Fallo al sincronizar registros CIP',
CodInt_1:=_int_in_,
CodInt_2:=_int_in_,
CodInt_3:=_int_in_,
CodInt_4:=_int_in_,
CodInt_5:=_int_in_,
CodReal_1:=_real_in_,
CodReal_2:=_real_in_);
END_IF;
"DB21_GESTION_SCADA".Sincronizacion.Informes[#for_i] := 0;
END_IF;
"DB21_GESTION_SCADA".TON_COCINA1_ESTADO[#for_i].IN := "DB21_GESTION_SCADA".Sincronizacion.Cocina1[#for_i] = 4 OR "DB21_GESTION_SCADA".Sincronizacion.Cocina1[#for_i] = 12;
IF "DB21_GESTION_SCADA".TON_COCINA1_ESTADO[#for_i].Q THEN
IF "DB21_GESTION_SCADA".Sincronizacion.Cocina1[#for_i] = 12 THEN
IF "DB21_GESTION_SCADA".Sincronizacion.Informes[#for_i] = 12 THEN
"FC8_ZC_TRAZA_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRAZA_CAT_1_SISTEMA",
User := '',
Codigo1 := 0,
Codigo2 := 0,
Codigo3 := 0,
Texto := 'Fallo al sincronizar registros CIP',
CodInt_1:=_int_in_,
CodInt_2:=_int_in_,
CodInt_3:=_int_in_,
CodInt_4:=_int_in_,
CodInt_5:=_int_in_,
CodReal_1:=_real_in_,
CodReal_2:=_real_in_);
END_IF;
"DB21_GESTION_SCADA".Sincronizacion.Cocina1[#for_i] := 0;
END_IF;
END_IF;
"DB21_GESTION_SCADA".TON_FERM_ESTADO[#for_i].IN := "DB21_GESTION_SCADA".Sincronizacion.Fermentadores[#for_i] = 4 OR "DB21_GESTION_SCADA".Sincronizacion.Fermentadores[#for_i] = 12;
IF "DB21_GESTION_SCADA".TON_FERM_ESTADO[#for_i].Q THEN
IF "DB21_GESTION_SCADA".Sincronizacion.Fermentadores[#for_i] = 12 THEN
IF "DB21_GESTION_SCADA".Sincronizacion.Informes[#for_i] = 12 THEN
"FC8_ZC_TRAZA_REGISTRO"(FechaHora := "DB1_SYS".FechaHoraActual,
Registrar := 1,
Categoria := "TRAZA_CAT_1_SISTEMA",
User := '',
Codigo1 := 0,
Codigo2 := 0,
Codigo3 := 0,
Texto := 'Fallo al sincronizar registros CIP',
CodInt_1:=_int_in_,
CodInt_2:=_int_in_,
CodInt_3:=_int_in_,
CodInt_4:=_int_in_,
CodInt_5:=_int_in_,
CodReal_1:=_real_in_,
CodReal_2:=_real_in_);
END_IF;
"DB21_GESTION_SCADA".Sincronizacion.Fermentadores[#for_i] := 0;
END_IF;
END_IF;
END_FOR;
END_REGION
REGION GESTION_TEMPORIZADORES
// ====================================================================================
// Gestion de marcas de bit de vida, reinicializamos los valores
FOR #for_i := 0 TO 1 DO
"FC2928_ZC_TON_INDV"(Pulso1seg := "DB1_SYS".Pulso1seg,
TON := "DB21_GESTION_SCADA".TON_ON[#for_i]);
"FC2928_ZC_TON_INDV"(Pulso1seg := "DB1_SYS".Pulso1seg,
TON := "DB21_GESTION_SCADA".TON_OFF[#for_i]);
"FC2928_ZC_TON_INDV"(Pulso1seg := "DB1_SYS".Pulso1seg,
TON := "DB21_GESTION_SCADA".TON_CIP_ESTADO[#for_i]);
"FC2928_ZC_TON_INDV"(Pulso1seg := "DB1_SYS".Pulso1seg,
TON := "DB21_GESTION_SCADA".TON_INFORME_ESTADO[#for_i]);
"FC2928_ZC_TON_INDV"(Pulso1seg := "DB1_SYS".Pulso1seg,
TON := "DB21_GESTION_SCADA".TON_COCINA1_ESTADO[#for_i]);
"FC2928_ZC_TON_INDV"(Pulso1seg := "DB1_SYS".Pulso1seg,
TON := "DB21_GESTION_SCADA".TON_FERM_ESTADO[#for_i]);
END_FOR;
END_REGION
*)
END_FUNCTION
@@ -0,0 +1,62 @@
DATA_BLOCK "DB7_TRZ"
TITLE = DB7_TRAZA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
NON_RETAIN
//Bloque de datos encargado de la trazabilidad.
VAR RETAIN
Gestion { S7_SetPoint := 'False'} : "UDT_ZC_TRZ_GESTION";
Buffer { S7_SetPoint := 'False'} : Array[0.."N_MAX_TRAZA_BUFFER"] of "UDT_ZC_TRZ_DATOS";
END_VAR
BEGIN
Gestion.HabilitacionGeneral := TRUE;
Gestion.EstadoConexion := TRUE;
Gestion.OrdenIniciar := FALSE;
Gestion.HabilitarCategoria[0] := TRUE;
Gestion.HabilitarCategoria[1] := TRUE;
Gestion.HabilitarCategoria[2] := TRUE;
Gestion.HabilitarCategoria[3] := TRUE;
Gestion.HabilitarCategoria[4] := TRUE;
Gestion.HabilitarCategoria[5] := TRUE;
Gestion.HabilitarCategoria[6] := TRUE;
Gestion.HabilitarCategoria[7] := TRUE;
Gestion.HabilitarCategoria[8] := TRUE;
Gestion.HabilitarCategoria[9] := TRUE;
Gestion.HabilitarCategoria[10] := TRUE;
Gestion.HabilitarCategoria[11] := TRUE;
Gestion.HabilitarCategoria[12] := TRUE;
Gestion.HabilitarCategoria[13] := TRUE;
Gestion.HabilitarCategoria[14] := TRUE;
Gestion.HabilitarCategoria[15] := TRUE;
Gestion.HabilitarCategoria[16] := TRUE;
Gestion.HabilitarCategoria[17] := TRUE;
Gestion.HabilitarCategoria[18] := TRUE;
Gestion.HabilitarCategoria[19] := TRUE;
Gestion.HabilitarCategoria[20] := TRUE;
Gestion.HabilitarCategoria[21] := TRUE;
Gestion.HabilitarCategoria[22] := TRUE;
Gestion.HabilitarCategoria[23] := TRUE;
Gestion.HabilitarCategoria[24] := TRUE;
Gestion.HabilitarCategoria[25] := TRUE;
Gestion.HabilitarCategoria[26] := TRUE;
Gestion.HabilitarCategoria[27] := TRUE;
Gestion.HabilitarCategoria[28] := TRUE;
Gestion.HabilitarCategoria[29] := TRUE;
Gestion.HabilitarCategoria[30] := TRUE;
Gestion.HabilitarCategoria[31] := TRUE;
Gestion.Etapa := 0;
Gestion.SP_Tiempo := 2;
Gestion.TiempoActual := 0;
Buffer[0].Registrar := 0;
Buffer[0].Categoria := 0;
Buffer[0].User := '';
Buffer[1].Registrar := 0;
Buffer[1].Categoria := 0;
Buffer[1].User := '';
END_DATA_BLOCK
@@ -0,0 +1,411 @@
FUNCTION "FC7_ZC_TRZ_GESTOR" : Void
TITLE = FC7_GESTOR_TRAZA
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 2.0
//Funcion para gestion de trazabilidad
VAR_INPUT
Pulso1Seg : Bool; // Pulso de 1 segundo
END_VAR
VAR_TEMP
for_i : Int; // Variable para bucles FOR
t_PosicionRegistro : Int; // Posicion del registro encontrado en el buffer
t_RegistroEncontrado : Bool; // Indicador de registro encontrado
t_TextoGenerado : Bool; // Indicador de texto generado en todos los Scadas
END_VAR
VAR CONSTANT
REPOSO : Int := 0; // ETAPA EN REPOSO
COMPROBACION_INICIAL : Int := 10; // ETAPA COMPROBACIONES INICIALES
GENERAR_TEXTO : Int := 20; // ETAPA GENERAR TEXTO
LANZAR_AVISO : Int := 30; // ETAPA LANZAR AVISO
ESPERA : Int := 40; // ETAPA ESPERA
BORRAR_TEXTO : Int := 50; // ETAPA BORRAR TEXTO
QUITAR_AVISO : Int := 60; // ETAPA QUITAR AVISO
BORRAR_DATOS : Int := 70; // ETAPA BORRAR DATOS
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 principal para la gestión de mensajes de trazabilidad y su envío estructurado a los sistemas de supervisión.
Las tareas que realiza son las siguientes:
- Búsqueda en orden inverso dentro del buffer para localizar y extraer el último registro con orden de registrar.
- Transferencia del registro a variables de gestión estáticas, formateo de cadenas de tiempo y usuario, y liberación de la posición ocupada en el buffer.
- Ejecución de la **Etapa Reposo**: Con un registro habilitado y conexión activa, lanza la orden de inicio de secuencia.
- Ejecución de la **Etapa Comprobaciones Iniciales**: Valida que la categoría del evento se encuentre en los límites permitidos (forzando una categoría de usuario si excede el índice), limpia las confirmaciones previas y espera la actualización temporal.
- Ejecución de la **Etapa Generar Texto**: Envía la orden de generación de textos a los SCADAs habilitados, transitando al recibir la confirmación de todos o al superar un tiempo de espera de seguridad.
- Ejecución de la **Etapa Lanzar Aviso**: Activa la señal de aviso correspondiente a la categoría de la trazabilidad procesada.
- Ejecución de la **Etapa Espera**: Retiene el aviso activo durante el tiempo configurado para garantizar su captura en los sistemas de supervisión.
- Ejecución de la **Etapa Borrar Texto**: Lanza la orden de limpieza de textos en pantalla a todos los SCADAs.
- Ejecución de la **Etapa Quitar Aviso**: Desactiva la señal de aviso y verifica mediante un tiempo corto que el evento ha caído.
- Ejecución de la **Etapa Borrar Datos**: Inicializa por completo los valores, códigos y textos del aviso actual, regresando a la etapa de reposo.
- Mapeo final de las activaciones de alarma a nivel de bit, agrupándolas en dos variables tipo `Word` para las 32 categorías y aplicando sus respectivas máscaras de habilitación individual.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | - |
| FB | - |
| DB | `DB7_TRAZA` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 31.08.2022 | (ABH) | Primera version. |
| 01.00.00 | 18.09.2023 | (ABH) | Se añade gestion de bits para 16 categorias. |
| 02.00.00 | 25.03.2026 | (ABH) | Se eliminan `String`. Se añaden 32 categorias. Se añade gestion de generacion de textos para Scada. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_TRAZA
// ==========================================================================================================
REGION GESTION_ULTIMO_REGISTRO
// Si la gestion esta habilitada, se busca el ultimo registro y se mueve a la variable de gestion de avisos para
// los sistemas de supervision
IF "DB7_TRZ".Gestion.HabilitacionGeneral AND #Pulso1Seg AND "DB7_TRZ".Gestion.Etapa = #REPOSO THEN
// Buscamos el ultimo registro
FOR #for_i := "N_MAX_TRAZA_BUFFER" TO 0 BY -1 DO
// Registro encontrado
IF "DB7_TRZ".Buffer[#for_i].Registrar = 1 THEN
#t_PosicionRegistro := #for_i;
#t_RegistroEncontrado := TRUE;
EXIT;
END_IF;
END_FOR;
// Si hemos encontrado el registro, copiamos a variable estatica
IF #t_RegistroEncontrado THEN
// Copiamos el los datos de la traza a variable estatica para tratarla
"DB7_TRZ".Gestion.Aviso.Datos := "DB7_TRZ".Buffer[#t_PosicionRegistro];
// Quitamos el registro del buffer
"DB7_TRZ".Buffer[#t_PosicionRegistro].Registrar := 0;
"DB7_TRZ".Buffer[#t_PosicionRegistro].Categoria :=
"DB7_TRZ".Buffer[#t_PosicionRegistro].CodInt_1 :=
"DB7_TRZ".Buffer[#t_PosicionRegistro].CodInt_2 :=
"DB7_TRZ".Buffer[#t_PosicionRegistro].CodInt_3 :=
"DB7_TRZ".Buffer[#t_PosicionRegistro].CodInt_4 :=
"DB7_TRZ".Buffer[#t_PosicionRegistro].CodInt_5 := 0;
"DB7_TRZ".Buffer[#t_PosicionRegistro].CodReal_1 :=
"DB7_TRZ".Buffer[#t_PosicionRegistro].CodReal_2 := 0.0;
"DB7_TRZ".Buffer[#t_PosicionRegistro].TimeStamp := DTL#1970-01-01-00:00:00;
"DB7_TRZ".Buffer[#t_PosicionRegistro].User := '';
END_IF;
END_IF;
END_REGION GESTION_ULTIMO_REGISTRO
// ==========================================================================================================
REGION GESTION_ORDEN_INICIAR
// =============================================================================
// Si existe registro a trazar, lanzamos la orden de iniciar la secuencia
IF "DB7_TRZ".Gestion.Aviso.Datos.Registrar = 1 AND "DB7_TRZ".Gestion.Etapa = #REPOSO AND "DB7_TRZ".Gestion.EstadoConexion THEN
// Lanzamos la orden de iniciar la secuencia de registro
"DB7_TRZ".Gestion.OrdenIniciar := TRUE;
END_IF;
END_REGION GESTION_ORDEN_INICIAR
END_REGION GESTION_TRAZA
// ==========================================================================================================
REGION SECUENCIA_TRAZA
// En caso de deshabilitar la trazabilidad, ponemos el estado en borrar datos.
IF NOT "DB7_TRZ".Gestion.HabilitacionGeneral AND "DB7_TRZ".Gestion.Etapa <> #REPOSO THEN
"DB7_TRZ".Gestion.Etapa := #BORRAR_DATOS;
END_IF;
// Gestion trazabilidad
CASE "DB7_TRZ".Gestion.Etapa OF
// =============================================================================
// Etapa reposo
#REPOSO:
REGION #REPOSO
// Con orden de iniciar, vamos a etapa comprobaciones iniciales
IF "DB7_TRZ".Gestion.OrdenIniciar THEN
"DB7_TRZ".Gestion.Etapa := #COMPROBACION_INICIAL;
"DB7_TRZ".Gestion.OrdenIniciar := FALSE;
END_IF;
END_REGION
// =============================================================================
// Etapa comprobaciones iniciales
#COMPROBACION_INICIAL:
REGION #COMPROBACION_INICIAL
// Si el registro actual no tiene orden de registrar, lo borramos yendo a etapa borrar datos
IF "DB7_TRZ".Gestion.Aviso.Datos.Registrar = 0 THEN
"DB7_TRZ".Gestion.Etapa := #BORRAR_DATOS;
ELSE
// Esperamos a que tenga los datos actualizados en scada/hmi
IF #Pulso1Seg THEN
"DB7_TRZ".Gestion.TiempoActual += 1;
IF "DB7_TRZ".Gestion.TiempoActual >= "DB7_TRZ".Gestion.SP_Tiempo THEN
"DB7_TRZ".Gestion.TiempoActual := 0;
// En caso de tener una categoria fuera de indices, formazos a categoria usuario
IF "DB7_TRZ".Gestion.Aviso.Datos.Categoria < "TRZ_CAT_1_SYS" OR "DB7_TRZ".Gestion.Aviso.Datos.Categoria > "TRZ_CAT_32_USER" THEN
"DB7_TRZ".Gestion.Aviso.Datos.Categoria := "TRZ_CAT_32_USER";
END_IF;
// Limpiamos las confirmaciones de texto generado por seguridad antes de esperar
FOR #for_i := 0 TO "N_MAX_SCADA" DO
"DB7_TRZ".Gestion.Aviso.TextoGenerado[#for_i] := FALSE;
END_FOR;
"DB7_TRZ".Gestion.Etapa := #GENERAR_TEXTO;
END_IF;
END_IF;
END_IF;
END_REGION
// =============================================================================
// Etapa generar texto
#GENERAR_TEXTO:
REGION #GENERAR_TEXTO
// Lanzamos la orden de generar textos a sistemas scada que esten habilitados
FOR #for_i := 0 TO "N_MAX_SCADA" DO
"DB7_TRZ".Gestion.Aviso.GenerarTexto[#for_i] := "DB7_TRZ".Gestion.Aviso.HabilitarHmiScada[#for_i];
END_FOR;
// Activamos marca de texto generado OK desde Scada, antes de realizar las comprobaciones.
#t_TextoGenerado := TRUE;
// Comprobamos si los Scadas estan habilitados y tenemos confirmacion de que el texto se ha generado correctamente
FOR #for_i := 0 TO "N_MAX_SCADA" DO
IF "DB7_TRZ".Gestion.Aviso.HabilitarHmiScada[#for_i] AND NOT "DB7_TRZ".Gestion.Aviso.TextoGenerado[#for_i] THEN
#t_TextoGenerado := FALSE;
END_IF;
END_FOR;
// Comprobacion de fin etapa
IF #t_TextoGenerado THEN
// Tenemos confirmacion de todos los Scadas que han generado el texto correctamente
"DB7_TRZ".Gestion.TiempoActual := 0;
"DB7_TRZ".Gestion.Etapa := #LANZAR_AVISO;
// Reseteamos las ordenes de generar texto
FOR #for_i := 0 TO "N_MAX_SCADA" DO
"DB7_TRZ".Gestion.Aviso.GenerarTexto[#for_i] := FALSE;
END_FOR;
ELSE
// Esperamos un tiempo de seguridad si no tenemos respuesta desde Scadas
IF #Pulso1Seg THEN
"DB7_TRZ".Gestion.TiempoActual += 1;
IF "DB7_TRZ".Gestion.TiempoActual >= "DB7_TRZ".Gestion.SP_TiempoTexto THEN
"DB7_TRZ".Gestion.TiempoActual := 0;
// Reseteamos las ordenes de generar texto
FOR #for_i := 0 TO "N_MAX_SCADA" DO
"DB7_TRZ".Gestion.Aviso.GenerarTexto[#for_i] := FALSE;
END_FOR;
"DB7_TRZ".Gestion.Etapa := #LANZAR_AVISO;
END_IF;
END_IF;
END_IF;
END_REGION
// =============================================================================
// Etapa lanzar aviso
#LANZAR_AVISO:
REGION #LANZAR_AVISO
// Activamos orden de lanzar el aviso
"DB7_TRZ".Gestion.Aviso.Aviso["DB7_TRZ".Gestion.Aviso.Datos.Categoria] := TRUE;
"DB7_TRZ".Gestion.Etapa := #ESPERA;
END_REGION
// =============================================================================
// Etapa espera
#ESPERA:
REGION #ESPERA
// Esperamos un tiempo para que el aviso se haya registrado en todos los sistemas de supervision
IF #Pulso1Seg THEN
"DB7_TRZ".Gestion.TiempoActual += 1;
IF "DB7_TRZ".Gestion.TiempoActual >= "DB7_TRZ".Gestion.SP_Tiempo THEN
"DB7_TRZ".Gestion.TiempoActual := 0;
"DB7_TRZ".Gestion.Etapa := #BORRAR_TEXTO;
END_IF;
END_IF;
END_REGION
// =============================================================================
// Etapa borrar texto
#BORRAR_TEXTO:
REGION #BORRAR_TEXTO
// Lanzamos orden de borrar texto a todos los Scadas
"DB7_TRZ".Gestion.Aviso.BorrarTexto := TRUE;
// Reseteamos los valores del aviso actual
"DB7_TRZ".Gestion.Aviso.Datos.Registrar := 0;
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_1 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_2 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_3 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_4 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_5 := 0;
"DB7_TRZ".Gestion.Aviso.Datos.CodReal_1 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodReal_2 := 0.0;
"DB7_TRZ".Gestion.Aviso.Datos.User := '';
"DB7_TRZ".Gestion.Aviso.Datos.TimeStamp := DTL#1970-01-01-00:00:00;
// Saltamos de etapa tras un tiempo
IF #Pulso1Seg THEN
"DB7_TRZ".Gestion.TiempoActual += 1;
IF "DB7_TRZ".Gestion.TiempoActual >= "DB7_TRZ".Gestion.SP_Tiempo THEN
"DB7_TRZ".Gestion.TiempoActual := 0;
"DB7_TRZ".Gestion.Aviso.BorrarTexto := FALSE;
"DB7_TRZ".Gestion.Etapa := #QUITAR_AVISO;
END_IF;
END_IF;
END_REGION
// =============================================================================
// Etapa quitar aviso
#QUITAR_AVISO:
REGION #QUITAR_AVISO
// Desactivamos el aviso
"DB7_TRZ".Gestion.Aviso.Aviso["DB7_TRZ".Gestion.Aviso.Datos.Categoria] := FALSE;
// Saltamos de etapa tras un tiempo
IF #Pulso1Seg THEN
"DB7_TRZ".Gestion.TiempoActual += 1;
IF "DB7_TRZ".Gestion.TiempoActual >= 2 AND NOT "DB7_TRZ".Gestion.Aviso.Aviso["DB7_TRZ".Gestion.Aviso.Datos.Categoria] THEN
"DB7_TRZ".Gestion.TiempoActual := 0;
"DB7_TRZ".Gestion.Etapa := #BORRAR_DATOS;
END_IF;
END_IF;
END_REGION
// =============================================================================
// Etapa borrar datos
#BORRAR_DATOS:
REGION #BORRAR_DATOS
// Reseteamos los valores del aviso actual
"DB7_TRZ".Gestion.Aviso.Datos.Registrar := 0;
"DB7_TRZ".Gestion.Aviso.Datos.Categoria := 0;
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_1 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_2 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_3 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_4 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodInt_5 := 0;
"DB7_TRZ".Gestion.Aviso.Datos.CodReal_1 :=
"DB7_TRZ".Gestion.Aviso.Datos.CodReal_2 := 0.0;
"DB7_TRZ".Gestion.Aviso.Datos.User := '';
"DB7_TRZ".Gestion.Aviso.Datos.TimeStamp := DTL#1970-01-01-00:00:00;
"DB7_TRZ".Gestion.Etapa := #REPOSO;
END_REGION
END_CASE;
END_REGION SECUENCIA_TRAZA
// ==========================================================================================================
REGION ACTIVACION_ALARMAS
// Activamos los avisos en WORD segun la categoria y su habilitacion
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X0 := "DB7_TRZ".Gestion.HabilitarCategoria[0] AND "DB7_TRZ".Gestion.Aviso.Aviso[0];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X1 := "DB7_TRZ".Gestion.HabilitarCategoria[1] AND "DB7_TRZ".Gestion.Aviso.Aviso[1];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X2 := "DB7_TRZ".Gestion.HabilitarCategoria[2] AND "DB7_TRZ".Gestion.Aviso.Aviso[2];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X3 := "DB7_TRZ".Gestion.HabilitarCategoria[3] AND "DB7_TRZ".Gestion.Aviso.Aviso[3];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X4 := "DB7_TRZ".Gestion.HabilitarCategoria[4] AND "DB7_TRZ".Gestion.Aviso.Aviso[4];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X5 := "DB7_TRZ".Gestion.HabilitarCategoria[5] AND "DB7_TRZ".Gestion.Aviso.Aviso[5];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X6 := "DB7_TRZ".Gestion.HabilitarCategoria[6] AND "DB7_TRZ".Gestion.Aviso.Aviso[6];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X7 := "DB7_TRZ".Gestion.HabilitarCategoria[7] AND "DB7_TRZ".Gestion.Aviso.Aviso[7];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X8 := "DB7_TRZ".Gestion.HabilitarCategoria[8] AND "DB7_TRZ".Gestion.Aviso.Aviso[8];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X9 := "DB7_TRZ".Gestion.HabilitarCategoria[9] AND "DB7_TRZ".Gestion.Aviso.Aviso[9];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X10 := "DB7_TRZ".Gestion.HabilitarCategoria[10] AND "DB7_TRZ".Gestion.Aviso.Aviso[10];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X11 := "DB7_TRZ".Gestion.HabilitarCategoria[11] AND "DB7_TRZ".Gestion.Aviso.Aviso[11];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X12 := "DB7_TRZ".Gestion.HabilitarCategoria[12] AND "DB7_TRZ".Gestion.Aviso.Aviso[12];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X13 := "DB7_TRZ".Gestion.HabilitarCategoria[13] AND "DB7_TRZ".Gestion.Aviso.Aviso[13];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X14 := "DB7_TRZ".Gestion.HabilitarCategoria[14] AND "DB7_TRZ".Gestion.Aviso.Aviso[14];
"DB7_TRZ".Gestion.Aviso.AvisoWord[0].%X15 := "DB7_TRZ".Gestion.HabilitarCategoria[15] AND "DB7_TRZ".Gestion.Aviso.Aviso[15];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X0 := "DB7_TRZ".Gestion.HabilitarCategoria[16] AND "DB7_TRZ".Gestion.Aviso.Aviso[16];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X1 := "DB7_TRZ".Gestion.HabilitarCategoria[17] AND "DB7_TRZ".Gestion.Aviso.Aviso[17];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X2 := "DB7_TRZ".Gestion.HabilitarCategoria[18] AND "DB7_TRZ".Gestion.Aviso.Aviso[18];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X3 := "DB7_TRZ".Gestion.HabilitarCategoria[19] AND "DB7_TRZ".Gestion.Aviso.Aviso[19];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X4 := "DB7_TRZ".Gestion.HabilitarCategoria[20] AND "DB7_TRZ".Gestion.Aviso.Aviso[20];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X5 := "DB7_TRZ".Gestion.HabilitarCategoria[21] AND "DB7_TRZ".Gestion.Aviso.Aviso[21];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X6 := "DB7_TRZ".Gestion.HabilitarCategoria[22] AND "DB7_TRZ".Gestion.Aviso.Aviso[22];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X7 := "DB7_TRZ".Gestion.HabilitarCategoria[23] AND "DB7_TRZ".Gestion.Aviso.Aviso[23];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X8 := "DB7_TRZ".Gestion.HabilitarCategoria[24] AND "DB7_TRZ".Gestion.Aviso.Aviso[24];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X9 := "DB7_TRZ".Gestion.HabilitarCategoria[25] AND "DB7_TRZ".Gestion.Aviso.Aviso[25];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X10 := "DB7_TRZ".Gestion.HabilitarCategoria[26] AND "DB7_TRZ".Gestion.Aviso.Aviso[26];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X11 := "DB7_TRZ".Gestion.HabilitarCategoria[27] AND "DB7_TRZ".Gestion.Aviso.Aviso[27];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X12 := "DB7_TRZ".Gestion.HabilitarCategoria[28] AND "DB7_TRZ".Gestion.Aviso.Aviso[28];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X13 := "DB7_TRZ".Gestion.HabilitarCategoria[29] AND "DB7_TRZ".Gestion.Aviso.Aviso[29];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X14 := "DB7_TRZ".Gestion.HabilitarCategoria[30] AND "DB7_TRZ".Gestion.Aviso.Aviso[30];
"DB7_TRZ".Gestion.Aviso.AvisoWord[1].%X15 := "DB7_TRZ".Gestion.HabilitarCategoria[31] AND "DB7_TRZ".Gestion.Aviso.Aviso[31];
END_REGION ACTIVACION_ALARMAS
END_FUNCTION
@@ -0,0 +1,106 @@
FUNCTION "FC8_ZC_TRZ_REGISTRO" : Void
TITLE = FC8_TRAZA_REGISTRO
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : ABH
FAMILY : ZeusControl
VERSION : 1.0
//Funcion para almacenar una traza en el buffer de trazas.
VAR_INPUT
FechaHora {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // Fecha y hora actual
Registrar : USInt; // Registrar trazabilidad
Categoria : Int; // Categoria de trazabilidad
User : String; // Usuario actual
CodInt_1 : Int; // Codigo entero 1
CodInt_2 : Int; // Codigo entero 2
CodInt_3 : Int; // Codigo entero 3
CodInt_4 : Int; // Codigo entero 4
CodInt_5 : Int; // Codigo entero 5
CodReal_1 : Real; // Codigo real 1
CodReal_2 : Real; // Codigo real 2
END_VAR
VAR_TEMP
t_dummy : Int; // Variable temporal para su uso en programa
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 almacenar de forma secuencial un nuevo evento de trazabilidad dentro del buffer de registros.
Las tareas que realiza son las siguientes:
- Verifica que la gestión general de trazabilidad se encuentre habilitada en el sistema.
- Desplaza el contenido actual del buffer de trazas una posición hacia abajo mediante la instrucción `MOVE_BLK_VARIANT`, liberando el primer índice.
- Convierte la marca de tiempo de entrada (formato `DTL`) a una cadena de texto (`String`) haciendo uso de la función `FC15006_ZC_DTL_TO_STRING`.
- Almacena los parámetros del nuevo registro (orden de registrar, marca de tiempo, categoría, códigos numéricos enteros y reales) en la posición inicial (índice 0) del buffer.
- Valida la cadena de caracteres correspondiente al usuario: si se encuentra vacía, registra una cadena de espacios por defecto; en caso contrario, asigna el nombre de usuario proporcionado en la entrada.
---
### Dependencias Requeridas
| Tipo | Elementos |
|------|-----------|
| FC | `FC15006_ZC_DTL_TO_STRING` |
| FB | - |
| DB | `DB7_TRAZA` |
| UDT | - |
---
### Historial de Cambios
| Versión | Fecha | Técnico | Cambios |
|----------|------------|---------|---------|
| 00.00.01 | 31.08.2022 | (ABH) | Primera version. |
| 00.00.02 | 25.03.2026 | (ABH) | Se añade datos nuevos de UDT. |
| 01.00.00 | 21.05.2026 | (ABH) | Se cambia la variable fechaactual de string a DTL. Se reduce el usuario actual a 15 caracteres. |
*)
END_REGION DESCRIPCION
// ==========================================================================================================
REGION GESTION_REGISTRO_TRAZA
// Si la trazabilidad esta habilitada
IF "DB7_TRZ".Gestion.HabilitacionGeneral THEN
// Desplazamos los datos una posicion hacia abajo
#t_dummy := MOVE_BLK_VARIANT(SRC := "DB7_TRZ".Buffer, COUNT := INT_TO_UDINT(("N_MAX_TRAZA_BUFFER")), SRC_INDEX := 0, DEST_INDEX := 1, DEST => "DB7_TRZ".Buffer);
// Guardamos el nuevo registro en la primera posicion
"DB7_TRZ".Buffer[0].Registrar := #Registrar;
"DB7_TRZ".Buffer[0].TimeStamp := #FechaHora;
"DB7_TRZ".Buffer[0].Categoria := #Categoria;
"DB7_TRZ".Buffer[0].CodInt_1 := #CodInt_1;
"DB7_TRZ".Buffer[0].CodInt_2 := #CodInt_2;
"DB7_TRZ".Buffer[0].CodInt_3 := #CodInt_3;
"DB7_TRZ".Buffer[0].CodInt_4 := #CodInt_4;
"DB7_TRZ".Buffer[0].CodInt_5 := #CodInt_5;
"DB7_TRZ".Buffer[0].CodReal_1 := #CodReal_1;
"DB7_TRZ".Buffer[0].CodReal_2 := #CodReal_2;
IF #User = '' THEN
"DB7_TRZ".Buffer[0].User := ' ';
ELSE
"DB7_TRZ".Buffer[0].User := #User;
END_IF;
END_IF;
END_REGION GESTION_REGISTRO_TRAZA
END_FUNCTION