> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prodexlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sistema de Eventos

> Temas, variables de estado, ganchos de eventos y oyentes de eventos: las primitivas para la coordinación entre componentes y el comportamiento dinámico.

## Visión general

El Sistema de Eventos (Event System) es lo que eleva a ProDex de un simulador de flujo de procesos a un sistema capaz de modelar lógica operativa sofisticada. Se construye sobre **cuatro** primitivas:

* **Temas (Topics)**: mensajería pub/sub entre componentes
* **Variables de estado (State Variables)**: estado mutable en tiempo de ejecución que vive en el modelo
* **Ganchos de evento (Event Hooks)**: acciones que se disparan en los eventos del ciclo de vida del propio componente
* **Oyentes de evento (Event Listeners)**: acciones que se disparan cuando se emite un tema suscrito

Juntos habilitan patrones que no se pueden expresar solo con flujo basado en conexiones: producción por demanda (kanban), contrapresión, cambios de capacidad por turno y coordinación entre componentes. Un usuario que solo conoce fuentes, procesos y sumideros está aprovechando una fracción de lo que la plataforma puede modelar.

Cada componente de flujo en el Modelador (Modeler) expone tanto una sección de **Event Hooks** como una de **Event Listeners** en su panel de configuración: son objetos distintos de primera clase, no variantes de la misma cosa. Los Temas y las Variables de estado se gestionan en el modal **Lookups**, abierto desde el botón **Lookups** en la parte inferior del panel Library del Modelador. El modal tiene cuatro pestañas: *Constants*, *Lookup Tables*, *State Variables* y *Topics*.

## Temas

Un **Tema** es un canal nombrado al que los componentes pueden publicar y suscribirse. Cuando un componente publica en un tema, cada componente que tenga un Event Listener suscrito a ese tema reacciona.

Los Temas permiten coordinación que no puede expresarse solo a través de conexiones de flujo: un componente puede disparar comportamiento en otro sin estar conectado directamente.

Crea y renombra Temas en la pestaña **Topics** del modal Lookups. Una vez que un Tema existe, los componentes publican en él mediante una acción `emit` y se suscriben añadiendo un Event Listener cuyo campo `topic` lo referencia.

<Warning>
  **Los Temas no llevan carga útil.** Una acción `emit` toma solo un nombre de tema; un Event Listener se dispara solo con el nombre del tema sin datos de mensaje. Si un oyente downstream necesita información sobre *por qué* se emitió el tema, el patrón canónico es asignar una variable de estado justo antes del emit y que el oyente lea esa variable de estado.
</Warning>

**Ejemplo, señal kanban:** cuando un búfer (Buffer) downstream cae por debajo de un umbral, un event hook emite el tema `replenish`. Una Fuente (Source) upstream tiene un Event Listener suscrito a `replenish` que ejecuta una acción `release`, generando más material.

## Variables de estado

Una **Variable de estado** es un valor mutable y nombrado que vive en el modelo (no en ninguna entidad individual). Las variables de estado persisten a través de las entidades: retienen su valor a medida que las entidades fluyen por el modelo, y pueden leerse desde cualquier expresión.

Usa variables de estado para cualquier cosa que represente el estado del *sistema* en lugar del estado de una sola entidad: turno actual, contadores de tiempo de actividad de máquinas, niveles de inventario, señales de demanda, promedios móviles.

Define Variables de estado en la pestaña **State Variables** del modal Lookups. Cada una tiene un nombre, un `type` (uno de `number`, `text`, `boolean`) y un `initial_value` **obligatorio** que coincida con el tipo declarado. Una vez definidas, léelas por nombre desde cualquier expresión y escríbelas con una acción `assign` en cualquier Event Hook, Event Listener o acción programada.

### Variables de estado vs. atributos de entidad

* **Atributo de entidad**: pertenece a una entidad específica y se mueve con ella a través del flujo. Se define en el tipo de entidad.
* **Variable de estado**: pertenece al modelo y persiste independientemente de las entidades. Se define a nivel del modelo a través del modal Lookups.

Si quieres que el mismo valor sea visible para cada componente en cada momento, usa una variable de estado. Si quieres que el valor siga a una entidad específica mientras se mueve por el flujo, usa un atributo.

## Event Hooks

Un **Event Hook** es una regla en un componente que dispara acciones cuando ocurre uno de los eventos del ciclo de vida *de ese mismo componente*. Los hooks viven en la sección **Event Hooks** del panel de configuración de cada componente de flujo: elige el evento de un desplegable, opcionalmente añade una condición y configura una o más acciones.

### Eventos canónicos por componente

Cada evento solo es válido en tipos de componentes específicos y ejecuta sus expresiones en un [contexto](/es/reference/expressions#expression-contexts) específico. Las etiquetas de la UI eliminan el prefijo `on_` y usan espacios: `entity created` en la UI corresponde a `on_entity_created` en el esquema.

| Componente  | Eventos                                                | Contexto                                                                        |
| ----------- | ------------------------------------------------------ | ------------------------------------------------------------------------------- |
| Source      | `entity created`                                       | entidad-única (la entidad recién creada)                                        |
| Combiner    | `entity consumed`, `batch assembled`, `entity created` | entidad-única / multi-entidad (para `batch assembled`) / entidad-única (salida) |
| Separator   | `entity consumed`, `entity created`, `batch created`   | entidad-única / entidad-única (cada salida) / multi-entidad (todas las salidas) |
| Transformer | `entity consumed`, `entity created`                    | entidad-única                                                                   |
| Buffer      | `entity entered`, `entity exited`                      | entidad-única                                                                   |
| Station     | `entity entered`, `entity exited`                      | entidad-única                                                                   |
| Process     | `process started`, `process completed`                 | entidad-única                                                                   |
| Router      | `entity routed`                                        | entidad-única                                                                   |
| Resource    | `slot freed`, `slot working`                           | sin-entidad                                                                     |
| Sink        | `entity terminated`                                    | entidad-única                                                                   |

Algunas cosas a notar:

* **`entity entered` / `entity exited`** solo se disparan en Buffer y Station. Los Procesos no los emiten: usa `process started` / `process completed`. Los Routers no los emiten: usa `entity routed`.
* **`slot freed` / `slot working`** son de Recursos (Resource), no de Estaciones. La denominación es inusual: `slot working` se dispara cuando un slot pasa de FREE a asignado.
* **`batch created`** es un evento del Separator en contexto multi-entidad sobre las salidas. El evento paralelo del Combiner es `batch assembled`, también multi-entidad pero sobre las entradas.
* **El filtro `entity_type`** solo es válido en `on_entity_consumed` del Combiner y `on_entity_created` del Separator. Otros event hooks se disparan para cada entidad independientemente del tipo.

<Note>
  **El orden de ejecución de los hooks no está definido cuando múltiples hooks se disparan en el mismo evento.** Si dos hooks en el mismo componente se suscriben al mismo evento, el orden en que se ejecutan no está especificado: no te bases en que los efectos secundarios de uno se vuelvan visibles para el otro dentro de un solo evento.
</Note>

### Tipos de acción

Cuando un hook se dispara, ejecuta una o más acciones. El desplegable Action ofrece (etiquetas de UI en código, nombres de esquema entre paréntesis):

* **`assign`**: escribir en una variable de estado. Ejemplo: `assign(CURRENT_SHIFT, "night")` o `assign(WIP_COUNT, WIP_COUNT + 1)`.
* **`emit`**: publicar un mensaje en un tema. Ejemplo: `emit("replenish")`.
* **`pause`**: pausar un componente (deja de aceptar y procesar).
* **`resume`**: reanudar un componente pausado.
* **`release`** (`release_entity`): liberar una entidad a demanda. Válido en Fuentes (libera una nueva entidad) y Búferes (libera la siguiente entidad encolada de un búfer en modo de pausa o retención).
* **`set capacity`** (`set_capacity`): cambiar la capacidad de un Recurso en tiempo de ejecución.

### Restricciones de acción por componente

No todas las acciones son válidas en todos los componentes:

| Componente                                                       | `assign` | `emit` | `release` | `pause` | `resume` | `set capacity` |
| ---------------------------------------------------------------- | -------- | ------ | --------- | ------- | -------- | -------------- |
| Source                                                           | ✓        | ✓      | ✓         | ✓       | ✓        |                |
| Buffer                                                           | ✓        | ✓      | ✓         | ✓       | ✓        |                |
| Resource                                                         | ✓        | ✓      |           |         |          | ✓              |
| Station, Process, Router, Sink, Combiner, Separator, Transformer | ✓        | ✓      |           |         |          |                |

En resumen: `set capacity` solo es válido en Resource. `release / pause / resume` solo son válidos en Source y Buffer. `assign` y `emit` son válidos en todas partes.

### Condiciones

Cada event hook tiene un campo `condition` opcional: una expresión booleana del DSL que se evalúa cada vez que se dispara el evento. Si es falsa, se omite la lista de acciones. La condición se ejecuta en el **mismo contexto que las acciones** (entidad-única para hooks vinculados a entidad, sin-entidad para hooks de slot de recurso, multi-entidad para hooks de lote).

Las condiciones viven a nivel de hook, no de acción: las acciones de un hook se ejecutan todas o se omiten todas.

## Event Listeners

Un **Event Listener** es una regla en un componente que dispara acciones cuando se emite un **tema** suscrito, desacoplado de cualquier evento del ciclo de vida. Los oyentes viven en la sección **Event Listeners** del panel de configuración de cada componente de flujo: un hermano de los Event Hooks, con su propio botón *Add Listener*.

Cada oyente tiene:

* **`topic`** *(obligatorio)*: el nombre de un tema declarado
* **`condition`** *(opcional)*: expresión booleana del DSL
* **`actions[]`**: los mismos seis tipos de acción que los Event Hooks, con las mismas restricciones por componente

<Note>
  **Los Event Listeners siempre se ejecutan en contexto sin-entidad, independientemente del componente anfitrión.** Un oyente en una Source no puede referenciar atributos de entidad, aunque el hook `on_entity_created` de una Source sí pueda. Los oyentes son reacciones a señales a nivel de modelo, no a entidades específicas: no hay ninguna entidad en alcance a menos que una acción la traiga explícitamente (por ejemplo, `release`).
</Note>

### Hooks vs. Listeners

Las dos primitivas de reacción se ven similares pero se comportan de manera diferente:

|               | Event Hook                                                | Event Listener                                            |
| ------------- | --------------------------------------------------------- | --------------------------------------------------------- |
| Disparado por | Evento del ciclo de vida del propio componente            | Emisión de tema                                           |
| Contexto      | Varía según el evento (sin-entidad / única / multi)       | Siempre sin-entidad                                       |
| Dónde vive    | Arreglo `event_hooks[]` del componente                    | Arreglo `event_listeners[]` del componente                |
| Suscripción   | Implícita (el componente dispara su propio ciclo de vida) | Explícita (el campo `topic` referencia un tema declarado) |
| Cuándo usarlo | Reaccionar al comportamiento *de este componente*         | Reaccionar a señales de coordinación a nivel de modelo    |

### Las suscripciones son estáticas

Las suscripciones se declaran en tiempo de construcción del modelo y nunca cambian en tiempo de ejecución. No hay acción `subscribe` ni `unsubscribe`. Si necesitas que diferentes oyentes estén activos bajo diferentes condiciones, contrólalo con el campo `condition` del oyente.

## ModelNode y el Sistema de Eventos

Los [Nodos de modelo (Model Nodes)](/es/reference/model-nodes) pueden hospedar **Event Listeners** pero no Event Hooks de ciclo de vida: el esquema acepta un arreglo `event_hooks[]` en un ModelNode, pero no hay eventos canónicos de ciclo de vida definidos para ModelNodes, por lo que cualquier hook autorizado fallaría la validación. Los oyentes (basados en tema) funcionan completamente y son la herramienta correcta para coordinación a nivel de ModelNode.

## Validación de loop-back

ProDex previene tres patrones específicos descontrolados:

1. **Bucles directos propios**: el hook `on_entity_created` de una Source no puede ejecutar una acción `release`; el hook `on_entity_exited` de un Buffer no puede ejecutar una acción `release`. Estas dos combinaciones específicas están prohibidas por el validador.
2. **Aciclicidad del grafo de temas**: el grafo dirigido de emisiones de temas (donde existe una arista del tema A al tema B cuando un oyente de A emite B) debe ser acíclico. Los ciclos se capturan en la validación, antes de que se ejecute el simulador.
3. **Los ciclos del grafo de flujo están explícitamente permitidos.** Enrutar entidades de vuelta a través de un Proceso upstream (bucles de retrabajo) es un modelo válido: el validador solo marca bucles dirigidos por eventos, no ciclos del grafo de conexiones.

## Acciones programadas

Un [Calendario (Schedule)](/es/reference/schedules) también puede disparar acciones en momentos específicos de la simulación, independientemente de cualquier evento del ciclo de vida del componente. Las acciones programadas son más restringidas que las acciones de Event Hook:

* **Solo `assign` y `emit`** son válidas como acciones programadas.
* Las acciones vinculadas a componente (`pause`, `resume`, `set capacity`, `release`) no están disponibles en calendarios. Usa un `emit` programado a un tema, luego conecta un Event Listener que escuche ese tema y realice la acción vinculada al componente.

Esta restricción mantiene los calendarios declarativos: un calendario declara "en el tiempo T, el mundo está en estado X", y los cambios de estado se propagan a través del sistema de eventos.

## Patrones comunes

**Kanban / Producción por demanda.** Los componentes downstream señalan a las fuentes upstream cuando necesitan más material. Combina una variable de estado (WIP actual) con un Event Hook que emite a un tema cuando WIP cae por debajo de un umbral, más un Event Listener en la Source que escucha el tema y ejecuta una acción `release`.

**Contrapresión.** Cuando un búfer downstream está lleno, pausa o ralentiza las llegadas upstream. Un Event Hook observa los cambios de nivel del búfer y actualiza una variable de estado que lee la expresión de tasa de llegada de la Source.

**Cambios de capacidad por turno.** Los Recursos cambian de capacidad por hora del día. Un `emit` programado en un tema `shift_start_night` se dispara en el límite del turno; un Event Listener en el Recurso escucha ese tema y ejecuta `set capacity` con el valor de la noche.

**Coordinación entre componentes.** Dos componentes distantes que no están conectados por flujo aún pueden coordinarse a través de un tema compartido. Uno publica, el otro reacciona vía un Event Listener.

## Cuándo recurrir al Sistema de Eventos

La mayoría de los modelos simples no necesitan eventos: una Source, unos cuantos Procesos, un Sumidero (Sink) y estás funcionando. Recurre a los eventos cuando:

* Necesitas comportamiento que abarca múltiples componentes sin flujo directo
* Necesitas que el modelo reaccione a reglas operativas (calendarios de turnos, cambios de prioridad, picos de demanda)
* Estás modelando sistemas lean o de demanda donde downstream pide a upstream
* Necesitas rastrear y reaccionar a métricas transversales (WIP móvil, utilización)

Si te encuentras duplicando lógica en muchas expresiones para coordinar comportamiento, esa es una señal para introducir una Variable de estado o un Tema.
