Effective Arc
Idioms for safe and predictable Arc automations, with the reason for each.
These idioms make Arc automations safe and predictable. Each one gives the reason in one or two sentences.
Sequences and stages
Put safety checks first
When more than one transition in a stage is true in the same cycle, the first one in the code wins. Put the abort checks at the top of each stage, and the normal transitions after them.
stage pressurize {
1 -> press_vlv_cmd
tank_pt > 450 => abort
abort_cmd == 1 => abort
tank_pt > 300 => hold
}See conditions and transitions.
Watch for unsafe values in a stage, not in a gate
A write or a wait needs no stage. But while a sequence waits at a gate
(tank_pt > 300 => next) or a time.wait, nothing else in that sequence runs. A stage
runs all its flows at the same time, so its safety checks stay active while it waits.
See steps of a sequence.
Keep a test and its abort in one sequence
Make each test one sequence of named stages, and reach the abort stage by name from each stage that needs it. This keeps the test and its safety actions together. A separate top-level abort also works: a transition to it stops the whole sequence that holds the transition. Never put the abort stage right after a write, a wait, or a gate step, because those steps move on to the next step in the code. See write a test sequence.
Add a timeout to each stage that waits
A stage that waits for a value that never comes holds the test forever. Add
time.wait(duration=60s) => abort as the last transition of the stage. See
add a timeout.
Show the stage to operators
The Console does not show which stage runs. Write a number for each stage to a channel, and show it with a state indicator on the schematic. See start a sequence from a schematic.
Conditions and values
Compare values explicitly
Write start_cmd == 1, not start_cmd. A number used as a condition works, but the
compiler warns:
numeric conditions are deprecated; use an explicit comparison like x != 0.
Use => for conditions and -> for values
-> passes every value, true or false. => passes only new true values.
tank_pt > 300 -> next compiles with no warning, but moves on at the first tank_pt
value, even when it is false. Use => for every transition. See
-> and =>.
Return the type of the channel
A function that writes to a uint8 command channel must return u8. A bool result
does not compile:
func bang_bang output type bool does not match channel heater_cmd value type u8. Use
u8(on) to turn a bool into 0 or 1.
Plot a value before you set a limit
Write a computed value, such as a rate of change, to a channel and plot it during a real run before you choose an abort limit. A derivative of a noisy sensor is very noisy, so smooth the sensor first. See compute values from channels.
Control
Give automations a lower authority than operators
An automation writes at 255 if you do not set an authority, so an operator cannot take
control from it. Add authority 100, and raise it to 255 only in the abort stage. See
control authority. Then a start
button on a schematic with a higher authority takes the valves from the automation. See
troubleshoot control.
Always give the authority line a default
authority (vent_vlv_cmd 100) without a default leaves the other channels, and the
index channels, at 255. An operator who takes vent_vlv_cmd then loses every write to
it. Write authority (100 vent_vlv_cmd 200) instead. See
control authority.
Stop a test before you redeploy
A redeploy starts the automation again from the start, and every $= value is lost. A
sequence that starts at deploy, such as true => press, goes back to its first stage,
which can close valves with the tank under pressure. A sequence that a button starts
stops and waits for the next click.
Functions
Keep side effects out of functions with no upstream trigger
A function call with no upstream trigger that reads a channel runs in every cycle while
its stage is active. A call to ranges.end or status.set inside such a function
repeats in every cycle. Call those functions directly in the stage, with literal or
variable arguments: ranges.end(key=range_key). See
mark each run with a range.
Keep state in the right place
A $= variable in a function keeps its value between calls, but goes back to its first
value each time its stage starts. To keep a value across stages, declare it with $= in
the sequence. See
entering a stage.