5. Messaging & Signals
Everything about moving data: message types and wildcards, signals, dataflow timing parameters, filters, and message tagging.
Message types and wildcards
Every message has a dot-notation type: ASCII parts of any length, grouped semantically with increasing specificity left to right — input.video.raw, input.audio.raw, input.audio.normalised. Types are applied to posts and used by triggers/subscriptions. Messages that match no subscription are discarded, unless the post sets a ttl (time-to-live) to keep them available in shared memory.
Trigger types match exactly or with wildcards:
<trigger name="AnyAudio" type="input.audio.*" />
<trigger name="AnyRaw" type="input.*.raw" />
Because crank code refers only to trigger/post names, rerouting is spec-only: change a trigger's type from input.audio.raw to input.audio.smoothed and the module's code is untouched.
Signals
Signals are the fast lane: no subscription checking, no complicated routing. Think of a conductor's stick beating time — every subscriber to signal A triggers shortly after it is sent. Signals are independent of contexts and are typical in simulations: do the work for a time step, then wait for the next beat.
<module name="Stepper">
<signal name="Input" type="My.Signal.2" />
<crank name="signal" function="Signal" />
<signal name="Output" type="My.Signal.1" />
</module>
The built-in Signal crank makes a signal-based ping-pong analogue possible with no user code. Only name and type are parsed on <signal> — it is deliberately minimal.
Dataflow parameters
| Parameter | Where | Effect |
|---|---|---|
after / delay | trigger | Delay delivery: <trigger ... after="5000" /> fires 5 s after the message arrives (after is an alias of delay). |
interval | trigger | Artificial trigger at a regular interval: <trigger name="Post" type="Regular.Post" interval="1000" />. |
to | post | Direct copy to a named component regardless of its subscriptions: <post type="Data.Web" to="WB1" />. The message is also still delivered to normal subscribers. |
maxage | trigger / retrieve | Only wake the module if the message is younger than this — skip triggers when lagging behind. (Not a <post> attribute.) |
interval timer only starts after the module has first been triggered by a real message. For a whiteboard, to="WB1" on a post and a type subscription on the whiteboard are equivalent ways to store the message.Filters
Triggers can narrow what they accept with attributes:
<trigger name="input" type="msg.1" maxage="20" /> <!-- skip messages older than 20 ms -->
<trigger name="input" type="msg.1" from="MyOtherModule" /> <!-- only from that component -->
<trigger name="input" type="msg.1" to="SomeModule" /> <!-- only messages addressed with to= -->
<trigger name="input" type="msg.1" tag="SomeTag" /> <!-- only messages carrying the tag -->
Advanced filtering: <filter> children
Every filter must pass for the trigger to fire. A worked example of each of the 8 operations (the last, maxage, is written as a trigger attribute, not a <filter> child):
<trigger name="input" type="msg.1" maxage="5000"> <!-- maxage: message younger than 5000 ms -->
<filter type="haskey" key="SomeKey" /> <!-- entry exists (any type; no value=) -->
<filter type="equals" key="camera" value="front-left" /> <!-- string ==, exact, case-sensitive -->
<filter type="notequals" key="state" value="disabled" /> <!-- string != -->
<filter type="equalsnumeric" key="mode" value="3" /> <!-- float == -->
<filter type="notequalsnumeric" key="channel" value="0" /> <!-- float != -->
<filter type="greaterthan" key="temp" value="80" /> <!-- float > -->
<filter type="lessthan" key="pressure" value="2.5" /> <!-- float < -->
</trigger>
| Filter type | Comparison |
|---|---|
haskey | The entry exists (key only, no value; any entry type — string, number or binary) |
equals | String equality — case-sensitive, exact, no wildcards |
notequals | String inequality |
equalsnumeric | Float equality, converting from string/int as needed (epsilon compare) |
notequalsnumeric | Numeric inequality |
greaterthan | Numeric greater-than (values equal within epsilon also pass, i.e. effectively ≥) |
lessthan | Numeric less-than (equal values also pass, i.e. effectively ≤) |
maxage (trigger attribute) | Message created less than maxage milliseconds ago; stored internally as a filter |
type= means the filter simply never rejects anything. Double-check spelling against the table above.The delivery pipeline
Tagging
Two complementary tagging mechanisms group messages of one topic or identity across differing types:
- String tags. Posts can carry
tag="mytag"; triggers can filter withtag="mytag". All downstream messages in the forward chain carry the tag, so a whole processing chain about one entity stays identifiable. - Propagating integer tags. A component can set a 32-bit unsigned integer tag on its output messages; the tag is automatically propagated to all consequent output messages of every receiving module — if A tags 10 and posts to B and C, everything B and C (and their descendants) produce carries 10.
<post name="Face" type="vision.face.found" tag="person42" />
<trigger name="ThisPerson" type="vision.*" tag="person42" />
tag="*" means "use my own name as the tag" — handy when instancing similar modules.Guaranteed vs non-guaranteed delivery
By default every posted message is guaranteed: the messaging layer keeps it until each matching subscriber has received it, even under load. Set guaranteed="no" on a <post> to mark it best-effort (internally MESSAGE_NON_GUARANTEED):
<post name="Frame" type="sensor.video.frame" guaranteed="no" />
Best-effort messages may be dropped rather than queued when a consumer is slow or a buffer is full — the sender is never blocked. Use it for high-rate, latest-value-wins streams (camera frames, telemetry ticks) where a stale frame is worthless and you would rather lose one than build a backlog. Keep the default (guaranteed) for commands, state changes and anything a downstream module must not miss.
guaranteed="no" is about buffering/backpressure behaviour, not about ordering or acknowledgement. Combine it with maxage on the consumer's trigger to also skip frames that arrived too late to be useful.