Automation
2026-03-09

FIFO/LIFO Buffer genérico para Siemens S7-1500

En automatización industrial es frecuente necesitar un buffer temporal de datos estructurados: registros de trazabilidad, eventos de producción, mensajes, resultados de test o snapshots de estado de máquina. En muchos proyectos PLC esta necesidad se resuelve con arrays ad hoc, índices manuales y lógica duplicada. Funciona, pero escala mal.

Para atacar ese problema desarrollé una librería de buffers FIFO/LIFO genéricos para Siemens S7-1500 en TIA Portal, escrita en SCL y pensada para reutilización real entre proyectos. El punto fuerte no es solo implementar una cola o una pila: es hacerlo de forma genérica, permitiendo almacenar cualquier tipo de dato de aplicación, normalmente representado como un UDT.

El código fuente completo de esta librería está disponible públicamente en GitHub: github.com/EidoAut/Fifo_Lifo .

Diagrama 1 · Flujo de dato genérico
UDT de aplicación
VARIANT
Serialize
Payload + TypeId
FIFO / LIFO
Deserialize

1) El problema de los buffers específicos de proyecto

Una implementación típica suele partir de un array de una estructura concreta de aplicación. Desde el punto de vista funcional sirve, pero desde el punto de vista arquitectónico la cola queda acoplada a un único modelo de datos.

TYPE UDT_Traceability :
STRUCT
    PartId     : STRING[32];
    StationId  : UINT;
    RecipeId   : UINT;
    TestOk     : BOOL;
END_STRUCT
END_TYPE

VAR
    Buffer : ARRAY[0..99] OF UDT_Traceability;
    Head   : INT;
    Tail   : INT;
    Count  : INT;
END_VAR

En cuanto el siguiente cliente necesita otra estructura de trazabilidad, o cuando el mismo buffer debe almacenar otro tipo de evento, toca volver a escribir la lógica. La consecuencia es conocida: duplicación, mantenimiento peor y una librería que en realidad no es librería.

Acoplamiento fuerte

La lógica del buffer depende directamente del tipo almacenado.

Poca reutilización

La misma solución acaba reescribiéndose en proyecto tras proyecto.

2) Objetivo de diseño

La librería se diseñó con cuatro objetivos claros:

  • desacoplar la estructura del buffer del tipo de dato almacenado,
  • poder reutilizar la misma lógica en proyectos distintos,
  • mantener un diseño limpio y natural dentro de TIA Portal,
  • dar soporte real a casos de trazabilidad y buffering asíncrono.

3) Modelo de payload genérico

La pieza clave es UDT_Eido_AnyItem, que representa el elemento realmente almacenado. Conceptualmente, el modelo es este:

TYPE UDT_Eido_AnyItem :
STRUCT
    Len     : UINT;
    TypeId  : UINT;
    Payload : ARRAY[0..511] OF BYTE;
END_STRUCT
END_TYPE
Campo Función
Len Longitud válida del payload serializado.
TypeId Identificador de tipo definido por la aplicación.
Payload Datos serializados en bruto como array de bytes.

Desde el punto de vista del buffer, todos los datos tienen la misma forma. Desde el punto de vista de la aplicación, cada payload puede corresponder a un UDT distinto.

4) Arquitectura: fachada pública + core interno

Otra decisión importante fue separar la API pública de la lógica interna. La librería se divide en dos capas.

Diagrama 2 · Arquitectura del software
Fachada pública
FB_Eido_Fifo
FB_Eido_Lifo

Interfaz limpia para la aplicación: comandos, estado y configuración.

Core interno
FB_Eido_Fifo_Core
FB_Eido_Lifo_Core

Serialización, almacenamiento, índices, validaciones y errores.

La fachada recibe comandos de nivel como Enq, Deq, Push, Pop y Clear. Internamente esos comandos se convierten a pulsos de un ciclo y se entregan al core. Esto simplifica el uso desde OB1 y evita mezclar la lógica de aplicación con detalles internos del almacenamiento.

Cfg      → parámetros de configuración
Cmd      → órdenes de operación
Status   → estado, diagnóstico y resultado
Store    → almacenamiento interno

5) FIFO: buffer circular

El FIFO está pensado para colas de eventos, registros de trazabilidad, buffering de telegramas y desacoplar generación y procesado de datos. La implementación usa un circular buffer, evitando desplazar arrays y manteniendo coste constante en las operaciones principales.

Diagrama 3 · Ejemplo de FIFO circular
Head = 2
Tail = 6
Count = 4
[0]
[1]
[2] ← HeadA
[3]B
[4]C
[5]D
[6] ← Tailnext
[7]
En un dequeue sale A y Head avanza. En un enqueue nuevo elemento entra en Tail y este índice rota al siguiente hueco disponible.

6) LIFO: pila reutilizable

Aunque comparte el mismo modelo de payload, el LIFO resuelve un patrón distinto. Es útil para históricos cortos, deshacer acciones, gestionar contextos o secuencias anidadas. La ventaja es que la aplicación consume la misma filosofía de uso, pero con semántica de pila.

7) Ejemplo de uso real en SCL

Una de las ventajas de la fachada pública es que el bloque se integra de forma limpia en el ciclo principal. Un ejemplo simplificado sería:

VAR
    FifoTrace     : FB_Eido_Fifo;
    TraceCfg      : UDT_Eido_Fifo_Cfg;
    TraceCmd      : UDT_Eido_Fifo_Cmd;
    TraceStatus   : UDT_Eido_Fifo_Status;
    TraceStore    : UDT_Eido_Fifo_Store;
    TraceRecord   : UDT_CustomerTraceability;
END_VAR

// Encolar un nuevo registro de trazabilidad
TraceCmd.Enq := NewPartCompleted;
TraceCmd.InTypeId := 1001;

FifoTrace(
    Cfg := TraceCfg,
    Cmd := TraceCmd,
    Status := TraceStatus,
    Store := TraceStore,
    DataIn := TraceRecord
);

// Consumir un registro cuando el sistema aguas abajo esté disponible
IF DbChannelReady AND NOT TraceStatus.Empty THEN
    TraceCmd.Deq := TRUE;
    TraceCmd.ExpectedTypeId := 1001;
END_IF;

Este patrón es especialmente útil cuando la generación del dato y su consumo no ocurren al mismo ritmo.

8) Trazabilidad: un caso de uso especialmente fuerte

Cada cliente suele tener su propio UDT con sus propios campos: identificador de pieza, timestamp, estación, receta, resultados, flags o códigos de error. Ese dato no es estable entre proyectos, pero la necesidad de bufferizarlo sí lo es.

TYPE UDT_CustomerTraceability :
STRUCT
    PartId       : STRING[32];
    Timestamp    : DATE_AND_TIME;
    StationId    : UINT;
    RecipeId     : UINT;
    TestResult   : BOOL;
    ErrorCode    : UINT;
END_STRUCT
END_TYPE

Ese UDT puede serializarse, almacenarse como payload y recuperarse más tarde sin modificar el buffer. Esto encaja muy bien cuando la red cae temporalmente, cuando el envío a base de datos es más lento que el ciclo de máquina o cuando hay que desacoplar adquisición y transmisión de datos.

9) Validación opcional con TypeId

El payload es genérico, pero eso no implica perder control. La librería permite validación opcional por TypeId. Si se activa, al extraer un elemento del buffer se comprueba que el tipo esperado coincide con el almacenado.

IF #Cfg.EnforceTypeId THEN
    IF #StoredItem.TypeId <> #Cmd.ExpectedTypeId THEN
        #Status.Error := TRUE;
        #Status.ErrorId := 5; // Type mismatch
    END_IF;
END_IF;

Esto aporta una capa adicional de seguridad cuando varios tipos de payload comparten infraestructura o cuando se quiere detectar de forma explícita un mal uso de la librería.

10) Ventajas reales del enfoque

  • Reutilización: una sola implementación sirve para muchos proyectos.
  • Desacoplamiento: la lógica del buffer no depende del UDT concreto.
  • Escalabilidad: nuevos tipos de datos no exigen reescribir la cola.
  • Arquitectura limpia: fachada pública, core interno y diagnóstico claro.
  • Aplicación práctica: muy útil para trazabilidad, eventos y comunicación asíncrona.

Conclusión

Una cola o una pila en PLC no son algo nuevo. Lo interesante aquí es la forma de construirlas: payload genérico, serialización, validación opcional de tipo y arquitectura modular. Eso convierte una necesidad recurrente de planta en una librería reusable de verdad.

En otras palabras: no es solo un FIFO/LIFO; es una pequeña infraestructura para transportar datos de aplicación dentro del PLC con más orden, más reutilización y menos acoplamiento.

← ALL INSIGHTSHOME →
META
POST
SLUG
fifo_lifo_buffer
LANG
es
TAG
Automation
Edita el contenido en content/insights/fifo_lifo_buffer.es.md y vuelve a ejecutar el build.