Skip to main content

Overview

A Schedule is what anchors a simulation to real-world timing. Without a schedule, a model runs in abstract time with synthetic arrivals, useful for exploratory work, but not for comparing against your actual floor. With a schedule, the simulation is anchored to real dates, real shift patterns, and real production plans. Material enters when your plan says it does, shift transitions fire at specific wall-clock times, and results are comparable directly to what your floor produced. Every serious use of ProDex involves at least one schedule. Most models have several: one for the “normal” plan, a few for alternative scenarios you’re comparing. If you’re orienting yourself in the simulation workflow first, start with the Simulation overview — this page is the deep reference for the timing layer. The mental model that makes schedules click is the two-plane split: the schedule carries when things change (releases fire, shifts turn over), while the model carries what each component does at each change. A three-shift pattern touching five resources needs three repeating scheduled emits plus 15 event listeners defined once in the model — not hundreds of individual capacity entries.

Choosing a Schedule

The schedule picker sits in the Modeler’s rail, next to the model picker. It lists every schedule in the factory, plus:
  • Default (no schedule): when selected, the model runs in abstract time with whatever stochastic arrivals the Sources define. There’s no schedule object behind this.
  • New Schedule: creates a fresh, empty schedule and switches to it.
  • Edit: opens the Schedule Editor for the currently selected schedule.
  • Delete: removes the currently selected schedule, after you confirm in a Delete Schedule dialog.
Edit and Delete only appear while a schedule (not Default) is selected.

The Schedule Editor

The Schedule Editor is a dialog titled Edit {schedule name} where you author a schedule’s timing and events. The top of the dialog has four fields that define the schedule:
  • Schedule Name: short, descriptive. This is what shows in the schedule picker.
  • Description: optional free text explaining what this schedule represents.
  • Start Time: the wall-clock moment simulation begins. Every event timestamp on this schedule must fall at or after Start Time.
  • End Time: the wall-clock moment simulation ends. If left blank, the simulation runs for the model’s configured Duration starting from Start Time. If set, End Time overrides Duration.
Schedule times are UTC. The engine only cares about intervals: an event’s simulation time is its timestamp minus Start Time, and the UI, charts, KPIs, and result tables all report these times in UTC exactly as entered. Enter the floor’s clock time as if it were UTC — don’t convert to real UTC. A time with no timezone is treated as UTC; a real-world offset like -06:00 does not localize anything, it shifts the displayed time off the floor’s clock (a release at 09:00-06:00 shows up as 15:00). This is the single most common schedule-authoring trap, especially in data you hand to Dexter.
Below the four fields, Timeline and Table tabs control how events are presented. Timeline lays events along a wall-clock axis, handy for spotting shift boundaries and spacing visually. Table lists every event in a sortable grid with Action Type, When, Repeats, Target, and Value columns, handy for bulk review and spotting timestamp typos. The Add Event menu adds one of three event types:
  • Material Release: entities entering the model at a specific time
  • Assign Variable: set a state variable at a specific time
  • Emit Event: publish a topic at a specific time

What a Schedule Contains

A schedule is a sequence of material releases and scheduled actions, each stamped with a specific absolute wall-clock time. The simulation runs from Start Time to End Time (or for the model’s configured Duration if End Time isn’t set), and each event fires when wall-clock simulation time reaches its timestamp. Every event timestamp is absolute, not a relative offset from Start Time.

Material Releases

A material release tells a Source or Buffer to emit one or more entities at a specific wall-clock time. Each release has:
  • Target: the Source or Buffer to release from, and the Entity type to release. Components inside a Model Node are listed after a divider; of those, only Buffers are valid targets, because a Source inside a sub-model receives entities through its parent Model Node. Sources and Buffers both accept only releases of their own entity type; any other pairing fails validation.
  • Value: the quantity, a whole number of at least 1.
  • When: the wall-clock moment the release fires (UTC). A release timestamped exactly at the schedule’s Start Time seeds initial WIP — material already on the floor when the run begins.
  • Repeats (optional): release the same quantity again on a fixed interval (see Recurring events).
  • Attributes: values to set on the newly created entities, via Edit Attributes. The Schedule Editor sets a fixed value per attribute. Dexter can also use the other assignment strategies here; a DSL Expression on a release runs in no-entity context — it can read constants, lookups, and state variables, but there’s no entity in scope yet.
Material releases are how you model “demand that actually happened.” Instead of a Source with a synthetic exponential arrival rate, you give it a sequence of real orders: at Nov 1, 2024 7:00 AM UTC release 50 units of Product A with priority="rush" and due_priority=5; at Nov 1, 2024 9:00 AM UTC release 30 units of Product B; and so on.
A Source’s arrival logic and material releases run in parallel. They aren’t mutually exclusive. A Source can keep producing stochastic arrivals while a schedule simultaneously injects specific batches at scheduled times, and event-driven release_entity actions can fire on top of both. If you want a schedule-only Source, leave its arrival logic empty.

Scheduled Actions

A scheduled action fires at a specific wall-clock time (When) without being tied to any component’s lifecycle event. The two action types are: Several actions can share the same time; they fire together. Dexter can also gate a scheduled action with a condition — a boolean DSL expression checked when the action is due, so it only fires if the condition is true. The Schedule Editor doesn’t show conditions; ask Dexter to add or change one. Component-bound actions (Pause, Resume, Set Capacity, Release Entity) are not valid as scheduled actions directly. If you need a scheduled capacity change, the pattern is:
  1. A scheduled Emit Event fires at the shift boundary: for example, publish topic shift_start_night.
  2. An Event Listener on the resource subscribes to that topic and sets capacity to the night value.
This separation keeps schedules declarative (“at Nov 1, 2024 6:00 PM UTC, announce that the night shift has started”) and lets Event Listeners handle the component-level side effects.
Scheduled actions run in no-entity, no-component context. SELF is not available (it’s restricted to event hooks and event listeners), and there’s no entity in scope. Expressions here can reach state variables, constants, lookup tables, simulation state (SIM_TIME, SIM_DURATION), and component query functions with explicit component ids.

Recurring Events

Shift boundaries and standing orders repeat, and you shouldn’t have to author dozens of identical events by hand. Both scheduled actions and material releases can repeat on a fixed interval: the event is due at its When time, then again at every interval, through the Until time (inclusive). Leave Until empty and it repeats to the schedule’s end time, or to the model’s duration when the schedule has none. The schedule stores one entry however long the run. In the Schedule Editor this is the Repeats column: turn on Repeat this event, set Every to a whole number and a unit (Seconds, Minutes, Hours, Days, or Weeks), and optionally an Until time.
  • The interval is a fixed length of time, so a daily event keeps its UTC time all year; it does not follow daylight-saving changes.
  • A weekday-only pattern is one event per weekday, each repeating every 1 week.
  • A condition on a repeating action is checked at every occurrence, and an occurrence that lands exactly on the end of the run is planned but does not fire.
Ask Dexter for a shift calendar and it builds one from repeating events.

Hierarchical Addressing

A release can target a Buffer inside a Model Node (not a Source; see Target above). In results data (such as the element_id column of schedule_definition), such a target is identified with the :: separator. Examples:
  • source_1: a top-level Source
  • model_node_1::buffer_1: a Buffer inside a Model Node
  • outer_node::inner_node::buffer_2: deeply nested
All segments are component ids, not display names. The :: separator is reserved, so no single component id may contain it.

The Shift Scheduling Pattern

Most factories have shift-based capacity that changes through the day and week. The idiomatic way to model this in ProDex combines schedules, topics, state variables, and event listeners:
  1. A shift state variable: CURRENT_SHIFT, type Text, initial value "day".
  2. Repeating Emit Event actions at shift boundaries: publish topic shift_day at the first day-shift start and shift_night at the first night-shift start, each repeating every day.
  3. An Event Listener on each affected resource subscribing to the shift topics: when shift_night is published, set CNC machine capacity to the night value and update CURRENT_SHIFT to "night".
  4. Expressions reference CURRENT_SHIFT: arrival rates, priority scoring, and anything else that varies by shift reads the state variable and branches accordingly.
This decomposition keeps the shift logic in one place (the Event Listener on the resource) and the shift timing in the schedule. Swap the schedule for a different shift plan and the capacity changes flow through automatically.

Reusing Schedules across Models

Schedules belong to the factory and can be reused across models. Each is a complete set of material releases and scheduled actions with its own Start Time and End Time; you pick which one to use from the schedule picker when running a simulation. Its component, entity, variable, and topic references must be valid for the model you run. Typical uses:
  • Baseline vs. stress scenarios: a “normal” schedule and a “demand surge” schedule, run separately and compared.
  • Historical replay: last month’s actual demand as one schedule, a planned demand forecast as another.
  • Shift variations: a standard five-day schedule and an alternative seven-day weekend-coverage schedule.
Each run pairs a model with the schedule it executed against. Comparing runs that vary the schedule lets you ask “how does this model configuration perform under different demand profiles?” Varying the model lets you ask “which configuration is best for this demand?”

Start Time and End Time

  • Start Time is the wall-clock moment simulation begins. Every material release and scheduled action timestamp must fall at or after Start Time.
  • End Time, if provided, is when simulation ends — it overrides the model’s configured duration. Useful for running the same schedule against a shorter or longer window without editing the schedule’s events.
  • If End Time is left blank, the simulation runs for the model’s configured Duration from Start Time.
  • Events timestamped after End Time (or after Start Time + Duration if no End Time is set) fall outside the run window and are never reached.
Under the hood the engine works in intervals, not zones: an event’s simulation time is simply its datetime minus Start Time. What you write is what every chart and KPI displays, in UTC — so stamp the whole schedule in one convention (floor clock + Z) and shift boundaries stay exactly where you put them.

Authoring Paths

There are two ways a schedule comes to exist, both producing the same schedule:
  1. The Schedule Editor — the dialog described above, best for small edits and review.
  2. Dexter — the fastest path for real data. Upload the raw material (a CSV of orders, a shift calendar, an ERP extract) and ask Dexter to build the schedule; it profiles the data and generates the releases and actions for you. This is how you turn an operational file into a schedule without hand-authoring each release.
Schedules are factory-wide: creating or editing one makes it available to every model in the factory. The currently selected schedule also rides along as context when you chat with Dexter, so “add a Saturday shift to this schedule” resolves without naming it. Before changing a shared schedule, consider the other models that use it.

Schedules in Results Data

Every run against a schedule materializes a schedule_definition table in the results dataframes, one row per atomic entry:
  • A scheduled action with N inner actions expands to N rows, discriminated by action_index; material releases are one row each.
  • entry_type is one of material_release, scheduled_assign, or scheduled_emit.
  • Columns are typed per entry: releases carry element_id, entity_type_id, quantity, and attributes_json (the JSON-encoded assignment dict — populated only on releases with per-release overrides); scheduled assigns carry variable_name and value_expr; scheduled emits carry topic. condition_expr (the unevaluated DSL condition, null when unconditional) applies to actions only, and action_index is null on release rows.
  • Every entry is registered up front, so a condition-gated action appears in the table even if its condition never evaluates true at runtime — the row records what the plan declared, not what fired.
This is the table to join when you’re asking “did the plan actually enter the model the way I wrote it?”

Tips

  • Every serious model needs a schedule. Synthetic arrivals are fine for the first pass; real schedules are required for comparable results.
  • Use absolute wall-clock timestamps consistently. It’s tempting to think in relative offsets (“release at +3600s”) but every event needs an absolute timestamp. If you’re sketching out a schedule on paper, compute the absolute timestamps from your base Start Time before entering them.
  • Gate scheduled actions with a condition (ask Dexter) to depend on runtime state, e.g., only emit a topic if a state variable is in a particular mode.
  • Keep schedule-level logic in the schedule, component-level logic in Event Listeners. The schedule emits the signal; the listener handles the side effects.
  • Use multiple schedules for scenario comparisons. Shift patterns, seasonal demand, what-if load cases, capacity planning under different assumptions — schedules are one axis of variation, model configuration the other.
  • Ordering of simultaneous events is not specified — don’t rely on it. If ordering matters between two events at the same timestamp, stagger them by a second or chain them through a topic.
  • Check provenance on planning numbers. Release quantities, assignment weights, and action conditions that Dexter authors carry a Data / Stated / Assumed marking in the Evidence panel — an assumptions review will surface any guesses in the plan.