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
enabledflag. - 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 intervalevent: 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.
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:
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¶
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.