You built the recipe table you wanted: the up and down keys walk the cursor, the selected row loads into your indicators, and a load data button writes new recipes back. Then, on the cell controller, two faults appear. The first press of load data takes seconds while the host pulls the subVI off disk, and every so often the whole interface freezes instead. Both faults come from how the subVI is called, not from the table code. This guide gives you the four-step lifecycle that removes them — one execution per scan, a Setup preload, an open by name and a single close in Cleanup — plus the tests that prove each step.
What you need
- A host that gives your inspection three states: Setup, which runs once before the first scan; the scan itself; and Cleanup, which runs once at the end. The custom UI steps, the run-VI step and these states are standard parts of the deployed inspection software.
- The recipe subVI with a defined connector pane, for this guide named recipe_array_mod.vi, and the exact spelling of its file name.
- The Open VI Reference function and the Call By Reference node from the standard function palette. Both appear in the environment's API reference.
- A strictly typed VI Refnum control, wired into the type specifier input, for type only.
- A stopwatch or a timestamp on the button, for the first-press test later.
The two failure modes: a hang and a slow first press
The hang is total. A custom UI step that contains a While loop with no stop condition never returns, and the host waits on that step forever, so the whole interface freezes rather than one button. The contract is simple: every custom UI step executes exactly once per scan and must return before the next scan. State that used to live in the loop, such as the previous cursor row and the previous key reading, still works; you carry it across scans in a shift register instead of inside a loop.
The slow first press has a different cause. Opening the reference by path makes the host read the VI, and the file dependencies behind it, from disk on the first call, which is the multi-second pause. The run-once contract shows up elsewhere on the bench too: ASCII instructions that execute once per scan and need retriggering follow the same rule, as described in ARL and AWA ASCII instructions execute once: retrigger each scan.
Step 1: run each step once, carry state in a shift register
- Delete the enclosing While loop from the custom UI step so its code runs once per scan and returns.
- Wire a shift register around the step code and move the loop's old state variables into it: the previous cursor row and the previous reading of the up and down keys.
- Compare the current key reading against the shift register value and act only when they differ. One key change then moves the cursor exactly one row, even while the operator holds the key.
- Write the new reading into the shift register so the next scan compares against this scan.
The step now returns control every scan, and the table keeps its memory between scans without owning a loop.
Step 2: preload the subVI in Setup with run set to False
- Add a run-VI step to the Setup state and point it at the recipe subVI file.
- Set the step's run input to False. The step loads the VI into memory but never executes it, so the subVI's file operations cannot fire during setup.
- Keep the preload ahead of any step that can open the custom UI, so the memory copy exists before the first press.
Setup is where a multi-second disk load costs nothing, because no operator watches that state. That is the whole trick behind a fast first press: pay the load in Setup, not at the button.
Step 3: open by name and call through a strictly typed refnum
- In the custom UI code, call Open VI Reference with a string holding just the VI name, recipe_array_mod.vi, with no path. The name resolves to the copy already in memory, which is why this open is fast.
- Wire a type specifier VI Refnum into the type specifier input, for type only. The function takes the connector pane information and the data type from that control and ignores the value wired through it.
- Build the specifier once, from any VI that has the connector pane you want. A strictly typed reference is bound to the connector pane pattern, not to one specific VI, which is why the bare refnum control shows no connector of its own until you feed it a refnum of the right type.
- Feed the reference into the Call By Reference node and wire the recipe data on its terminals. The node calls whichever VI matches the pattern and runs it synchronously, so the step returns when the call finishes.
Keep the file work inside the called subVI and build it from primitive functions; primitives are not counted as dependencies, which is what removes the dependency exceptions on the deployed host. If the subVI reaches for custom palettes or DLL paths, more than code loads at run time, as covered in how subVIs resolve custom palettes and DLL paths.
Step 4: close the reference once in Cleanup
- Add one custom UI step to the Cleanup state and pass the same refnum through it.
- Set its close input to True. Cleanup runs once, so the close happens exactly once per session.
- Guard the open on the UI side with a shift register so it fires on the first press only; every later press reuses the refnum the first one stored.
Hold yourself to a count of one: one open, many calls through it, one close. Open-once also keeps the reference list short enough to audit by eye at handover.
Worked example: one shift of load data presses on a 12-recipe table
Example inputs, sized to the recipe-table case: a table of 12 recipes with one press per load; 40 load data presses in an 8-hour shift; 3 s for a first open by path off disk, anchoring the multi-second load; 0.2 s for an open by name with the VI already in memory, inside the well-under-a-second target.
The rule: a dynamically opened reference stays open until something closes it, so what the host holds is opens minus closes.
Build A opens by path on every press and never closes. Opens: 40 presses × 1 refnum = 40 per shift. Closes: 0. Left open: 40 - 0 = 40 refnums at shift end, and 40 × 20 shifts = 800 across a month.
Build B preloads in Setup, opens once by name and closes once in Cleanup. Opens: 1. Closes: 1. Left open: 1 - 1 = 0, and the other 39 presses reuse the refnum. First press: 3 s - 0.2 s = 2.8 s saved, which is 2.8 / 3 ≈ 93% of the wait.
The decision: build B. If a first press still takes more than about a second, the preload did not happen, so fix Setup rather than the button code. Keep closes equal to opens: one refnum per subVI for the whole session.
Verify it worked
- Time the first press after the host loads, with the stopwatch or the timestamp on the button. Pass is well under a second.
- Run a full shift, then list the VIs the host keeps in memory with your development tools. The recipe subVI appears once, not once per press.
- Let Cleanup run and read its error output. Reopen the inspection and press load data immediately: no exception and no dependency error.
- Hold the up key across several scans. The cursor moves one row per scan change and never two, which proves the step ran once per scan.
- If behavior differs from run to run, leftover state is the usual suspect; start each run from a known state, the discipline described in 3D camera test flaky on leftover settings: start from a known state.
Common mistakes
- A path wired where a name belongs. Path makes the host re-read from disk and drag dependencies in; the bare file name finds the memory copy.
- A loosely typed refnum in the type specifier input. The Call By Reference node needs the connector pane carried by a strictly typed reference; a generic refnum has no pane to bind against.
- Close left at False in Cleanup. Every open then outlives the session, one refnum at a time.
- The old endless loop back in a step for a quick bench test. It hangs the host the first time that step runs on the floor.
- Renaming the subVI after the preload. The by-name lookup depends on the name, so keep the file name stable for the session.
- Inlining a helper that a dynamic call depends on. A helper you inline must keep its original call semantics, and the constraints are set out in SubVI inlining requires preserved call semantics.
- A table that keeps hurting you with file I/O on the controller. A serial recipe scheme with one command code per recipe is the lighter architecture, as laid out in RS232 vision recipe selection: one command code per recipe.
Where the custom UI runs
None of this is headless: the custom UI, the inspection and the reference lifecycle share one box on the line. A vision controller is that box, with camera LAN ports on one side, built-in light channels out and the display output your UI shows on. The MV-VC3301H-128G66 includes a VM6200Pro license, 8 GB of memory and 4 built-in light channels, so host and lighting drive from one unit.
Both controllers below carry 8 GB of memory and 4 built-in light channels:
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- LAN ports
- 3-4 LAN ports
- USB 3 ports
- 2 USB 3 ports
If you mount the host in an operator-facing cabinet, a display matters as much as the LAN ports; the industrial panel PCs from our sister site cover screens with built-in computing for that role. The same host can also start the inspection from your own program instead of a button press; the wiring is in start an inspection automatically from a custom program.
Before you ship the UI
- Zero endless loops in any custom UI step. The step must return every scan; one stuck step freezes the whole host.
- Setup preload present, run set to False. That pays the disk load where nobody waits.
- Open by bare file name, with a strictly typed VI Refnum in the type specifier input. The call binds to the connector pane pattern, not to a path.
- Closes equal opens. One open, one close, close True in Cleanup.
- First press timed under a second. That is the proof the VI was in memory.
- File name frozen for the session. The by-name lookup has nothing else to go on.
When the box under the UI needs choosing too, start from the vision controllers category, and you can contact the applications team to check a camera, light and controller plan against your cycle budget.
Questions
Do I need a full development system on the controller to call a subVI by reference?
No. The Open VI Reference function and the Call By Reference node belong to the development environment's standard function set, and the deployed inspection software already supplies the custom UI steps, the run-VI step and the Setup and Cleanup states the pattern uses. You build the custom UI where you develop it and deploy it with the inspection, so the reference lifecycle decides whether the call works, not an extra license.
Why does the first press still take seconds after I switched to opening by name?
The name lookup only helps when the VI is already in memory. If the Setup preload is missing, points at the wrong file, or ran with run set to True so it executed and errored, the first by-name open falls back to a disk load. Memory also starts empty after every host restart, so the preload has to live in Setup rather than in a utility you run by hand once.
How many references should the custom UI hold while it runs?
One per dynamically called subVI: open it once, call through it on every press, and close it once in Cleanup. Opening by name on every press also works, but each open returns a new refnum, so the host then holds one refnum per press until something closes them. At teardown, the number of open refnums should equal the number of closes you wired.
What breaks an Open VI Reference by name?
Two things in practice. Renaming the file after the Setup preload leaves the name string pointing at a VI that is no longer in memory, so the open falls back to disk. And loading two different VIs that share one file name makes the lookup ambiguous. Keep the file name stable for the session and unique across the folders the host loads.

