Why a scripted wait stalls Cellario
Cellario does not execute script steps concurrently; this avoids race conditions across scripts and orders, but it also means a long wait inside a script behaves as a blocking step. If the script waits for the full incubation time, the script step does not finish and control is not returned to the scheduler early enough to run other plates or orders.
The built-in alternatives are Pause Before/Pause After, or incubation by making the ODTC an eligible storage resource. Those options are not available when the plate moves must be performed by scripts. One attempted door-handling path also has a constraint: an after-move script may close the ODTC door after the arm places the plate and then immediately reopen it; for unsealed plates, even a brief close can be unacceptable because of carryover-contamination risk.
Decision path
| Condition | Use | Constraint |
|---|---|---|
| CellarioScheduler 4.3 available | Set the script Blocking Mode to Non-blocking/async in Protocol Designer | Async still blocks the current plate/thread; script must exit when the system is pausing |
| Earlier Cellario version | Loop the script step in the protocol and evaluate elapsed time on each iteration | Put the loop in the Cellario protocol, not inside the script |
| Multiple plates share ODTC/Bravo capacity | Track per-plate start time and extend loops dynamically | Do not hard-code loop count; fixed loops can alternate pauses and over-incubate |
| Native incubation is acceptable | Use Pause Before/Pause After or ODTC as eligible storage | Not compatible with script-only moves; check door behavior for unsealed plates |
Pre-4.3 pattern: protocol loop with per-plate timing
- Before the loop, store the plate arrival time at the ODTC in
api.Data.PerPlateData.PerStepDatais another available data scope, but PerPlateData matches the per-plate incubation requirement. - On each script execution, read the stored value and compare it with
DateTime.Now. - If the required incubation time has not elapsed, locate the plate’s Loop End step and increment
NumberOfLoopsby 1 so the protocol reruns the script. - Add a short in-script wait with
Thread.Sleep()orTask.Delay()to release the scheduler between evaluations; use a shorter interval for tighter timing and a longer interval to reduce diagnostic-log and order-event noise. - If elapsed time is at or above the target, do not increment
NumberOfLoops; the protocol exits the loop for that plate.
// Evidence-shaped pseudocode; adapt only to the available Cellario API objects.
start = api.Data.PerPlateData["OdtcStart"];
if (start == null) {
api.Data.PerPlateData["OdtcStart"] = DateTime.Now;
IncrementLoopEndNumberOfLoops(plate, 1);
Thread.Sleep(pollInterval);
} else if (DateTime.Now - start < targetIncubation) {
IncrementLoopEndNumberOfLoops(plate, 1);
Thread.Sleep(pollInterval);
} else {
// Target met: leave NumberOfLoops unchanged so the Cellario loop ends.
}
A test implementation used a 10 second loop delay, with 30 seconds considered to reduce debug-window output. Shorter polling reduces the chance of overshoot: if the last evaluation happens at 9:59 of a 10 minute target and the next check is one minute later, exit occurs near 11 minutes unless the wait interval is reduced.
Fixed loop counts fail with parallel plates
A fixed pattern such as 10 loops with a 1 minute pause can produce about 20 minutes of incubation when two plates alternate through two Bravos and two of four ODTCs. The same fixed count under-incubates a single plate, or the last plate of an odd-numbered run, to about 5 minutes in the reported setup. The corrective rule is to start with one loop and dynamically increment only while that plate’s elapsed ODTC time is below target.
CellarioScheduler 4.3 non-blocking scripts
CellarioScheduler 4.3 adds a script Blocking Mode parameter in Protocol Designer. Non-blocking async execution lets a long-running script proceed without blocking the scheduler from processing other plates, while still blocking the current plate/thread. Because async scripts run on a separate thread from the main scheduler thread, simulations can show gantt-chart variation; this is described as a known thread-behavior limitation.
An async script should include a loop or periodic system-state check and exit when the system is pausing. If it does not, the async script can prevent the system from pausing.
Verification and diagnostics
Use Run Time Viewer to confirm whether the scheduler switches to other work between script-loop iterations for a plate in the same thread, a different thread, or a different order. A v4.0.1.31 test observed task switching between script loops in those scenarios, while one field report saw no other tasks attempted; treat that difference as configuration-dependent until reproduced.
Check for a flow gate of 1 across the steps where concurrency is expected, and confirm whether all plates are constrained to the same thread. Watch diagnostic logs and order events for excessive traces when polling aggressively, and verify final ODTC residence time per plate rather than counting script executions.
FAQ
How do I pause one Cellario plate without stopping the whole system?
Before CellarioScheduler 4.3, loop the script step in the protocol, store ODTC start time in api.Data.PerPlateData, and increment the plate’s Loop End NumberOfLoops only until elapsed time reaches the target. In 4.3, set the script Blocking Mode to Non-blocking/async.
Why did my Cellario script loop still block other plates?
The loop may have been placed inside the script instead of in the Cellario protocol; moving the loop to the protocol allows Cellario to process plates between iterations. Also check Run Time Viewer for a flow gate of 1 or same-thread constraints.
How often should a Cellario timing loop poll?
Use a short Thread.Sleep()/Task.Delay() interval for tighter exit timing and a longer one to reduce diagnostic/order-event noise; reported values were 10 seconds in test and about 30 seconds to limit debug output. More frequent checks reduce overshoot beyond the incubation target.