Skip to content

Trigger

The trigger module evaluates stream expressions against live data and fires actions when a match occurs. Each trigger has exactly one matcher and one action.

Module id: trigger. Config type: trigger (<module>.trigger.<id>).

This is the primary way to execute any action on a RET device in response to live data: start measurements, send commands, publish events, or patch configuration.

Concepts

  • Trigger: one matcher and one action, with an enabled flag.
  • Matcher (streamExpr): a boolean expression on incoming updates.
  • Action: what happens on a match.
  • Flow: flower coordinates measurements. The trigger sends a publish request; it does not need to know whether a measurement is already running.

When an expression references more than one data channel or more than one messaging context, use interval-based evaluation (interval >= 0.2 s). Without an interval the expression re-runs on every incoming message, which is unpredictable when channels arrive independently.

NATS endpoints

None declared.

Trigger is a client:

Action Where it goes
measurement local.flower.cmd.flow.main.measurement_event_publish
command / jsonCommand any cmd subject named by cmdRef
patchConfig configurer patch
event event2 publish

algol trigger publish talks to flower, not to this module.


Expression language

Expressions use expr-lang and must evaluate to bool. They are compiled at config time; an unknown function or an invalid component/attribute ref is a configuration error.

Every code path - including every branch of if/else, &&, ||, and ternaries - must produce a bool. Returning a raw value from status_val without a comparison is a compile-time error.

Contexts

Trigger expressions subscribe to three contexts:

Context Purpose Functions
status Periodic telemetry snapshots status_val, status_vals, status_component_refs, status_timestamp, status_raw, status_raws
data_status Per-batch column statistics (min, max, sum, count) data_status_val, data_status_timestamp, data_status_raw
event Discrete occurrences on event2 event_val, event_vals, event_component_refs, event_timestamp, event_raw, event_raws

An event name is either a registered type or custom_<label>. Any other name is a configuration error. The same rule gates the event action's eventName.

Cadence:

  • data_status: one message per batch (batch size N at frequency F -> every N/F seconds)
  • status: the source module's publish interval
  • event: when they occur

Mix contexts or channels with different cadences -> use interval.

status_val(attributeRef, [bindingArg])

Current value of one attribute. nil if not yet received. Wildcard component_id ("module.name.*.attr") needs a second argument 'component_id='+<expr>.

status_val('gps.channel.position.lat') > 48.0
status_val('sensors.temp.*.value', 'component_id='+sensorId) > 80.0

status_vals / status_component_refs take a wildcard and return sorted slices. status_timestamp is the last measurement time. status_raw returns {name, tags, fields, ts}.

data_status_val(attributeRef, 'agg=<min|max|count|sum>')

Per-batch statistic. agg is required.

data_status_val('imu.accel.main.x', 'agg=max') > 9.8

event_val(attributeRef, [bindingArg])

Like status_val on event2. For a registered event the attribute is a top-level payload field, validated at config time:

event_val('sys.disk_mounted.disk01.targetDir') != nil

A nested payload keeps its shape in the expression (use ?. when the event may not have arrived). Time-series storage keeps only top-level scalars - expressions see the nested shape.

event_timestamp is the event's edge time. event_raw(...).fields is the payload.

Other functions

Function Meaning
proc_timestamp() Processing time of this evaluation
capture(key, value) Store value for the action; returns value. Reset each evaluation
store_set(key, value) / store_get(key) Persistent store for the trigger's lifetime. nil deletes
geo_distance([lat,lon], [lat,lon]) Haversine distance in metres
component_id(ref) Third segment of a component ref
hex_int(hex) Hex string (optional 0x prefix) to int, all 64 bits in two's complement - the decoder for bitmask status fields such as netprobe's successMask. Shift with bitushr, not bitshr, when the top bit may be set. Unparseable input is an evaluation error

let bindings are required when passing a dynamic value into a wildcard binding argument. All *_val / *_raw / *_timestamp functions return nil until data arrives - use ?? for defaults.

Examples

status_val('gps.speed.main.value') > 5.0
data_status_val('imu.accel.main.x', 'agg=count') >= 10
  && data_status_val('imu.accel.main.x', 'agg=max') < 2.0
geo_distance(
  [status_val('gps.channel.position.lat'), status_val('gps.channel.position.lon')],
  [47.3769, 8.5417]
) < 500.0
let armed = store_get('armed') ?? true;
let speed = status_val('gps.speed.main.value');
store_set('armed', speed <= 5.0 || nil) != nil
  && capture('speed', speed) > 5.0
  && armed

A streamExpr that matches a level re-fires on every evaluation. Use minWaitPeriod on the action, never -1 there. A real edge uses store_set / store_get in the expression.

Runtime config fields and action types: trigger config.