Flow Controller
Flow Controller is the central orchestrator in the LOPflow system. It manages a TouchDesigner component as a controllable multi-step process, exposing a clean API through an Internal OP shortcut (iop.LOPflow) that the target component can call to drive the flow forward.
Key Features
Section titled “Key Features”- Step sequence defined in the parameter panel with per-step duration and routing
- Three step behaviors: instant auto-advance, timed auto-advance, and wait-for-trigger
- Persistent flow context (
iop.LOPflow.Context) shared across all steps in a run - Chained callbacks — both assigned and DAT callbacks fire for every event
- Dashboard integration for real-time monitoring from LOP Studio
- Flow history and flow graph generation for debugging and visualization
- Unique process ID per run for tracking concurrent or sequential flows
How It Works
Section titled “How It Works”When you pulse Start, the controller generates a unique process ID, clears the context, and enters the first step. Each step runs onStepEnter, then behaves according to its duration setting:
- duration = 0 (default): the step auto-advances to the next step immediately after
onStepEntercompletes. - duration = -1: the step waits indefinitely. The flow only advances when
Next Stepis pulsed, or wheniop.LOPflow.NextStep()is called from the target component or a callback. - duration > 0 (seconds): the step waits for the specified time then auto-advances. A trigger can interrupt and advance early.
Each step also has a then setting controlling where the flow goes after the step completes:
- Next Step: advance to the following step (at the last step, this ends the flow)
- End Flow: immediately call
Finish() - Any step name from the sequence: jump directly to that step (enabling loops and branches)
Input/Output
Section titled “Input/Output”Inputs
Section titled “Inputs”None — the Flow Controller has no wired inputs.
Outputs
Section titled “Outputs”- Output 1: Not used for data; present for future integration.
- Output 2: Not used for data; present for future integration.
The primary integration path is the iop.LOPflow Internal OP shortcut installed on the target component, and the callbacks DAT.
First-time Setup
Section titled “First-time Setup”- Place a Flow Controller in your network.
- On the Setup page, set Target Component to the component you want to orchestrate.
- Give it a descriptive Flow Name.
- Pulse Setup to install the
LOPflowInternal OP shortcut on the target. The Setup Status field confirms success. - A callbacks DAT (named
{target}_flow_callbacks) is automatically created and docked to the target component. The Callbacks DAT field is also set automatically if a matching DAT is found docked to the target.
Defining Steps
Section titled “Defining Steps”- Go to the Steps page.
- Use the Step sequence to add steps. Each step has:
- label: Display name shown in the dashboard and used to auto-generate the step’s internal name.
- duration:
0(default) to auto-advance instantly, a positive number of seconds for timed advance, or type-1to wait indefinitely for a trigger. - then: Where to go after this step completes (
Next Step,End Flow, or a named step for branching/looping).
- Step names are auto-generated from the label (lowercased, spaces replaced with underscores). If you rename a step, the old name is tracked in history so
GoToStep('old_name')still resolves correctly.
Starting and Stopping a Flow
Section titled “Starting and Stopping a Flow”- Pulse Start on the Control page to begin. The Status field updates to
Runningand Current Step shows the active step. - Pulse Stop to abort the current run. Status returns to
Ready. - When a
wait-duration step is active, the Waiting field showsYes. Pulse Next Step to advance manually.
Calling the Flow API from the Target Component
Section titled “Calling the Flow API from the Target Component”Once set up, the target component drives flow progression by calling methods on iop.LOPflow:
# Advance to next step (use in onStepEnter for auto-type steps)iop.LOPflow.NextStep()
# Pause at current step waiting for external triggeriop.LOPflow.WaitForContinue(step_data={'status': 'Processing...'})
# Send a mid-step update without advancingiop.LOPflow.Update(step_data={'progress': 0.5})
# End the flow with a resultiop.LOPflow.Finish(result='Complete!', result_type='success')
# Jump to a specific step by nameiop.LOPflow.GoToStep('review')
# Read/write flow context (shared state across all steps)iop.LOPflow.Set('my_value', 42, source='callback:onStepEnter')val = iop.LOPflow.Get('my_value')These calls are made from inside callbacks — typically onStepEnter — in the callbacks DAT docked to the target component.
Flow Context
Section titled “Flow Context”The context is a persistent key-value store (iop.LOPflow.Context) that lives for the duration of one flow run. It is cleared automatically when Start is pulsed.
Every call to iop.LOPflow.Set(key, value) fires the onContextChange callback and pulses the OnContextChange reactive dependency. This lets FlowState operators and Script CHOPs observe context changes in real time.
Dashboard Integration
Section titled “Dashboard Integration”Enable Expose to Dashboard on the Setup page to make this controller visible in the LOP Studio Dashboard. When enabled, the dashboard receives real-time status updates whenever the flow state changes.
Enable Expose Flow Details to also include the full flow context, run history, and a flow graph (nodes and edges) in the dashboard payload. This is useful for debugging and visualization but adds data volume.
CHOP Integration
Section titled “CHOP Integration”A Script CHOP using flow_channels_scriptchop.py can be placed inside the Flow Controller to expose flow state as CHOP channels. The Script CHOP produces channels including:
on_start,on_finish,on_update,on_next_step— pulse events (momentary 0/1)current_step,max_steps,waiting_for_continue— step progressstatus_idle,status_running,status_completed,status_stopped,status_error— mutually exclusive status channelsflow_active,progress— derived convenience channels- Optional
result_length,step_data_length,last_update_timedata channels (disabled by default)
Callbacks Reference
Section titled “Callbacks Reference”All callbacks receive an info dict. All callbacks also receive ownerComp (the target component) in the dict.
| Callback | When it fires |
|---|---|
onFlowStart | Flow begins; receives process_id, inputs, list of steps |
onStepEnter | Entering each step; receives full step dict and metadata |
onStepExit | Exiting each step before advancing |
onNextStep | After advancing to the next step |
onWaitForContinue | When a step enters waiting state |
onFlowUpdate | When iop.LOPflow.Update() is called mid-step |
onContextChange | When iop.LOPflow.Set() changes a context value |
onFlowFinish | Flow ends; receives result and result_type |
onError | On unhandled errors |
onStepsChanged | When the Steps sequence parameters are modified in the UI |
The callback system uses chained callbacks: both any programmatically assigned callback and the DAT callback fire for every event, in that order. The DAT callback gets the final say on returnValue.
Usage Examples
Section titled “Usage Examples”Basic Three-Step Flow
Section titled “Basic Three-Step Flow”- Create a Flow Controller and set Target Component to your processing COMP.
- On the Setup page, pulse Setup.
- On the Steps page, add 3 steps:
- Step 0: label
Initialize, duration-1, thenNext Step - Step 1: label
Process, duration-1, thenNext Step - Step 2: label
Complete, duration0, thenEnd Flow
- Step 0: label
- In the callbacks DAT (auto-created next to your target comp), add logic to
onStepEnter:- When
step_name == 'initialize': prepare resources, then calliop.LOPflow.NextStep() - When
step_name == 'process': start work, calliop.LOPflow.WaitForContinue()until done - When
step_name == 'complete': read result from context
- When
- Pulse Start on the Control page.
Timed Auto-Advancing Steps
Section titled “Timed Auto-Advancing Steps”- Add a step with a label, set duration to
2.0(seconds), and then toNext Step. - The flow will automatically advance to the next step two seconds after entering this step.
- You can still advance early by calling
iop.LOPflow.NextStep()from within theonStepEntercallback before the timer fires.
Branching with the “then” Menu
Section titled “Branching with the “then” Menu”- Add multiple steps including a step named
reviewand a step namedretry. - On the
reviewstep, set then toretryusing the dropdown (step names appear in the menu after they are defined). - When this step completes, the flow jumps back to
retryinstead of advancing linearly.
Dashboard Monitoring
Section titled “Dashboard Monitoring”- On the Setup page, enable Expose to Dashboard.
- Set a descriptive Flow Name so it’s identifiable in the dashboard list.
- Optionally enable Expose Flow Details to see the full context dict and step history in the dashboard.
Troubleshooting
Section titled “Troubleshooting”Status shows “Not Setup” Pulse Setup on the Setup page. A Target Component must be set first.
Setup fails with “No available IOP slots” The target component’s IOP slot table (up to 10 slots) is full. Check if another Flow Controller is already installed on the same target, or use a different target component.
“Next Step” pulse has no effect
The flow must be running and a wait-duration step must be active. Check that Waiting shows Yes on the Control page.
Callbacks DAT is not found automatically
The auto-detection looks for a text DAT docked to the target component containing onFlowStart or onStepEnter. If your callbacks DAT has a different structure, set Callbacks DAT manually on the Setup page.
Step names are not resolving in GoToStep()
If a step was renamed (its label changed), the old slug name is stored in a rename history table (step_name_history). Old names resolve automatically. If you need to clear the history, this can be done via the ClearNameHistory() method from a script.
Flow restarts on project reload but state is lost
The controller persists its setup (IOP slot, target path, process ID) in component storage across reloads. However, in-progress flow execution state (which step was active) is not restored — the flow will show Ready rather than Running after a reload.
Parameters
Section titled “Parameters”Control
Section titled “Control”op('flow_controller').par.Start Pulse Start the flow
- Default:
False
op('flow_controller').par.Stop Pulse Stop the current flow
- Default:
False
op('flow_controller').par.Nextstep Pulse Advance to next step (when waiting)
- Default:
False
op('flow_controller').par.Status Str Current flow status
- Default:
"" (Empty String)
op('flow_controller').par.Currentstep Str Current step name and index
- Default:
"" (Empty String)
op('flow_controller').par.Waiting Str Whether flow is waiting for continuation
- Default:
"" (Empty String)
op('flow_controller').par.Lastresult Str Last flow result
- Default:
"" (Empty String)
op('flow_controller').par.Targetcomp OP The component to manage as a flow
- Default:
"" (Empty String)
op('flow_controller').par.Flowname Str Human-readable name for this flow
- Default:
"" (Empty String)
op('flow_controller').par.Setup Pulse Setup the Internal OP shortcut on target component
- Default:
False
op('flow_controller').par.Cleanup Pulse Remove the Internal OP shortcut from target component
- Default:
False
op('flow_controller').par.Setupstatus Str IOP shortcut setup status
- Default:
"" (Empty String)
op('flow_controller').par.Activeprocessid Str Current unique process ID
- Default:
"" (Empty String)
op('flow_controller').par.Exposeprocess Toggle Make this FlowController discoverable from LOP Studio Dashboard
- Default:
False
op('flow_controller').par.Callbackdat OP DAT with callback functions (onFlowStart, onStepEnter, etc.)
- Default:
"" (Empty String)
op('flow_controller').par.Exposeflowdetails Toggle Include context, history, and flow graph in dashboard data (for debugging/visualization)
- Default:
False
op('flow_controller').par.Step Sequence - Default:
0
op('flow_controller').par.Step0label Str - Default:
"" (Empty String)
op('flow_controller').par.Step0duration Float - Default:
0.0- Range:
- 0 to 1
- Slider Range:
- 0 to 1
Callbacks
Section titled “Callbacks”onFlowStartonFlowFinishonStepEnteronStepExitonNextSteponWaitForContinueonFlowUpdateonContextChangeonErroronStepsChanged
def onFlowStart(info):
# Called when the flow starts
# info contains: process_id, start_time, source, inputs, steps
# info['ownerComp'] - the FlowController component
return
def onFlowFinish(info):
# Called when the flow completes
# info contains: process_id, result, source, response_dict
return
def onStepEnter(info):
# Called when entering a step
# info contains: step, step_index, step_name, step_type, step_label,
# step_duration, step_then, step_data, source
#
# step_type is derived from duration:
# 'auto' = instant (duration 0) or timed (duration > 0)
# 'wait' = wait indefinitely (duration -1)
return
def onStepExit(info):
# Called when exiting a step
# info contains: step, step_index, step_name, step_type, source
return
def onNextStep(info):
# Called when advancing to the next step
# info contains: step, step_data, source
return
def onWaitForContinue(info):
# Called when flow pauses waiting for continuation
# info contains: step, step_data, update_data, source
return
def onFlowUpdate(info):
# Called when flow sends a mid-step update
# info contains: step, step_data, update_data, advance_step, source
return
def onContextChange(info):
# Called when a context value changes via iop.LOPflow.Set()
# info contains: key, value, old_value, source, step
# source examples: 'api', 'callback:onStepEnter', 'FlowTrigger:myTrigger'
return
def onError(info):
# Called when the flow encounters an error
# info contains: error, process_id
return
def onStepsChanged(info):
# Called when the Steps sequence parameters are modified
# info contains: steps, max_steps
return Changelog
Section titled “Changelog”v0.1.12026-03-16
Initial release
v0.1.02026-03-12
- Add flow history tracking with ring buffer audit trail - Add flow graph generation (nodes/edges) for dashboard visualization - Add Exposeflowdetails toggle for dashboard context/history/graph exposure - Add context change dedup via unique _id per Set() call - CurrentStep is -1 when not running - Add guards on all step transitions when flow not active
- Add Context system (DependDict) with Get/Set/ClearContext API - Add source parameter to all lifecycle methods for audit trail - Add OnContextChange dependency for reactive observers - Add onContextChange callback to emptyCallbacks - Update step_manager to propagate source through transitions - Add flow_context_design.md reference document
- Add extends field to manifest
- Add src/ modules (step_manager, dashboard_bridge, parameter_sync, chained_callbacks) - Add reference/ with implementation docs and test examples - Update README with proper description and structure - Fix manifest.json description and category
- Initial commit