Skip to main content

Overview

The Event System is what elevates ProDex from a process-flow simulator to a system that can model sophisticated operational logic. It’s built on three primitives:
  • Topics — pub/sub messaging between components
  • State Variables — mutable runtime state that lives on the model
  • Event Hooks — actions that fire on lifecycle events
Together they enable patterns that aren’t expressible with connection-based flow alone: pull-based production (kanban), backpressure, shift-driven capacity changes, cross-component coordination. A user who only knows about sources, processes, and sinks is using a fraction of what the platform can model.

Topics

A Topic is a named channel components can publish to and subscribe to. When a component publishes a message on a topic, any component subscribed to that topic receives the message and can react. Topics enable coordination that isn’t expressible through flow connections alone — one component can trigger behavior in another without being directly wired to it. Example — kanban signal: when a downstream buffer drops below a threshold, publish a replenish message on a topic. An upstream Source subscribed to that topic reacts by releasing more material.

State Variables

A State Variable is a named, mutable value that lives on the model (not on any individual entity). State variables persist across entities — they retain their value as entities flow through the model, and they can be read from any expression. Use state variables for anything that represents the state of the system rather than the state of a single entity: current shift, machine uptime counters, inventory levels, demand signals, rolling averages.

State Variables vs. Entity Attributes

  • Entity attribute — belongs to a specific entity and moves with it through the flow. Defined on the entity type.
  • State Variable — belongs to the model and persists independently of entities. Defined at the model level.
If you want the same value visible to every component at every moment, use a state variable. If you want the value to track a specific entity as it moves through the flow, use an attribute.

Event Hooks

An Event Hook is a rule that fires an action when a lifecycle event occurs on a component. Hooks connect lifecycle events to actions — the event determines when the hook fires, the action determines what it does.

Canonical Events

These are the lifecycle events components emit. Each event is only valid on specific component types, and each runs its condition and action expressions in a specific context.
EventFires OnContext
on_entity_createdSourcesingle-entity (the newly created entity)
on_entity_createdCombiner (output), Separator (output), Transformer (output)single-entity (the output entity)
on_entity_enteredBufferNode, Stationsingle-entity
on_entity_exitedBufferNode, Stationsingle-entity
on_process_startedProcesssingle-entity
on_process_completedProcesssingle-entity
on_entity_routedRoutersingle-entity
on_entity_consumedCombiner, Separator, Transformersingle-entity (the input being consumed)
on_batch_assembledCombinermulti-entity (all inputs in the batch)
on_batch_createdSeparatormulti-entity (all outputs being produced)
on_slot_freedResourceno-entity
on_slot_workingResourceno-entity
on_entity_terminatedSinksingle-entity
A few things to notice from this table that commonly trip people up:
  • on_entity_entered / on_entity_exited only fire on BufferNode and Station, not on every component that has entities passing through. Processes don’t emit them — use on_process_started / on_process_completed instead. Routers don’t emit them — use on_entity_routed.
  • on_slot_freed and on_slot_working are on Resources, not Stations. Stations have entity enter/exit events; Resources have slot events.
  • on_batch_created is a Separator event (one input becomes many outputs — multi-entity context over the outputs). Combiner’s output event is on_entity_created, in single-entity context.

Context Rules

The event’s context determines what identifiers your hook’s condition and action expressions can reference:
  • No-entity contextSIM_TIME, component-query functions, constants, state variables, and SELF (the component). No entity attributes.
  • Single-entity context — everything above plus entity attributes by bare name (priority, weight) and ENTITY_TYPE, ENTITY_AGE.
  • Multi-entity context — no-entity identifiers plus aggregation functions over the set of entities (SUM(weight), ANY(priority > 5)).
See Expression Contexts for the full model.

Action Types

When a hook fires, it executes an action. The valid actions:
  • assign — write to a State Variable. Example: assign(current_shift, "night") or assign(wip_count, wip_count + 1).
  • emit — publish a message to a Topic. Example: emit("replenish"). Triggers any subscribed components to react.
  • release_entity — release an entity on demand. Valid on Sources (releases a new entity) and BufferNodes (releases the next queued entity from a paused or hold-mode buffer). Used for pull-based patterns where release is driven by an event, not by arrival logic or buffer state alone.
  • pause — pause a component. The component stops accepting new entities and stops processing until resumed.
  • resume — resume a paused component.
  • set_capacity — change the capacity of a Resource at runtime. Used for shift-driven capacity patterns. (Not valid on Stations — to change station behavior via events, use pause/resume or model it as resource capacity.)

Cycle Detection

The Event System detects and rejects cycles and self-loops at validation time. Specifically:
  • A hook that listens for an event on component A and fires an action that triggers another event on component A, creating a loop, is rejected.
  • A topic that both publishes from and subscribes to the same component in a way that creates an infinite loop is rejected.
The reason: without this check, a small configuration mistake can produce a model that enters an infinite event loop at runtime. Cycle detection catches these before simulation starts.

Scheduled Actions

A Schedule can also fire actions at specific simulation times, independent of any component lifecycle event. Scheduled actions are more restricted than Event Hook actions:
  • Only assign and emit are valid as scheduled actions.
  • Component-bound actions (pause, resume, set_capacity, release_entity) are not available in schedules. Use a scheduled emit to a topic, then attach an Event Hook that subscribes to the topic and performs the component-bound action.
This restriction keeps schedules declarative — a schedule declares “at time T, the world is in state X,” and state changes propagate through the event system.

Common Patterns

Kanban / Pull-Based Production. Downstream components signal upstream sources when they need more material. Combine a State Variable (current WIP) with an Event Hook (publish to a topic when WIP drops below a threshold) and a subscription on the Source with a release_entity action. Backpressure. When a downstream buffer is full, pause or slow upstream arrivals. An Event Hook watches buffer level changes and updates a state variable that the Source’s arrival-rate expression reads. Shift-Driven Capacity Changes. Resources change capacity by time of day. A scheduled emit("shift_start_night") fires at the shift boundary; an Event Hook on the resource subscribes to that topic and runs set_capacity(resource, night_capacity). Cross-Component Coordination. Two distant components that aren’t connected by flow can still coordinate through a shared topic. One publishes, the other reacts.

When to Reach For the Event System

Most simple models don’t need events — a Source, a few Processes, a Sink, and you’re running. Reach for events when:
  • You need behavior that spans multiple components without direct flow
  • You need the model to react to operational rules (shift schedules, priority changes, demand surges)
  • You’re modeling lean or pull systems where downstream pulls from upstream
  • You need to track and react to cross-cutting metrics (rolling WIP, utilization)
If you find yourself duplicating logic in many expressions to coordinate behavior, that’s a signal to introduce a State Variable or Topic.