0.60

ReferenceAlarmsArc alarms

Write alarms in Arc

Set a status when a value passes a limit, clear it, latch it, and stop a test safely.

An Arc automation can watch channels and set a status when a condition is true. The Synnax Console shows each status change as a notification, and a PagerDuty alert task can send it on. This page shows the common alarm patterns. For every parameter of status.set, see the standard library.

Three rules apply to every pattern:

  • Use => to act when a condition is true. -> passes every value, true or false, so tank_pressure > 600.0 -> status.set{...} sets the status on every sample.
  • Import the modules that you use: import status, import stable, import time.
  • Digital channels in Synnax are u8. Compare them with == 1, and write 1 or 0.

Set a status at a limit

import status

tank_pressure > 600.0 => status.set{
    key_or_name="tank-pressure",
    variant="error",
    message="Above 600 psi",
}

This sets the status each time a batch of samples is above the limit, which can send many notifications. To send one notification for each change, use the next pattern.

Set and clear a status once

stable.for passes a value only after it stays the same for a duration, and only once for each change. Use two limits so that a value near one limit does not set and clear the status again and again:

import stable
import status

tank_pressure > 600.0 -> stable.for{duration=1s} => status.set{
    key_or_name="tank-pressure",
    variant="error",
    message="Above 600 psi",
}

tank_pressure < 580.0 -> stable.for{duration=1s} => status.set{
    key_or_name="tank-pressure",
    variant="success",
    message="Below 580 psi",
}

Both lines use the same key, so the operator sees one status that changes from Error to Success.

Always set a variant. When key_or_name names no status, Arc creates one with a new key. If a PagerDuty alert task watches the status, create the status first and use its key.

Latch an alarm until an operator clears it

Some alarms must stay on until an operator acknowledges them, even after the value comes back:

func latching_alarm{
    limit f64,
    ack chan u8,
} (value f64) u8 {
    latched $= u8(0)
    ack_signal := ack
    if ack_signal == 1 {
        latched = 0
    }
    if value > limit {
        latched = 1
    }
    return latched
}

tank_pressure -> latching_alarm{limit=600.0, ack=alarm_ack} -> latched_alarm

Connect alarm_ack to a button on a schematic, and latched_alarm to a light. The light stays on until the operator clicks the button.

Stop a test when an alarm occurs

Put the alarm checks in each stage of the sequence, and make the abort its own stage of the same sequence. The abort stage closes the valve, opens the vent, and sets a status that tells the operator why the test stopped.

import stable
import status
import time

sequence main {
    stage pressurize {
        tank_pressure > 700.0 => abort
        outlet_pressure > 500.0 => abort
        abort_btn == 1 => abort
        1 -> valve_cmd
        tank_pressure > 500.0 => next
    }
    stage hold {
        0 -> valve_cmd
        tank_pressure > 600.0 -> stable.for{duration=1s} => abort
        abort_btn == 1 => abort
        time.wait{duration=30s} => complete
    }
    stage complete {
        0 -> valve_cmd
    }
    stage abort {
        0 -> valve_cmd
        1 -> vent_valve
        status.set{
            key_or_name="tank_abort",
            variant="error",
            message="Test aborted",
        }
    }
}

start_cmd == 1 => main

A transition from inside the test to a top-level abort stops the whole test, including any nested sequences. Its alarm checks stop too. To keep watching after an abort, put the monitoring flow at the top level. A top-level => abort starts the abort and stops nothing else.