ReferenceControlArcEffective Arc

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.