Primera version

This commit is contained in:
2026-07-14 10:21:52 +02:00
parent 273e2be076
commit 430e3b4db0
222 changed files with 176320 additions and 0 deletions
@@ -0,0 +1,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