Plan Conditions
Triggers, events, and conditional logic
Plan Conditions
Reference for triggers, events, and conditional mechanisms that control Plan behavior and activation.
Pro Tip
Use debouncing and hysteresis for sensor triggers to avoid oscillation. A robot that rapidly switches between "obstacle detected" and "clear" states is harder to work with than one that uses a deadband - for example, trigger at 25cm but don't clear until 35cm.
Condition types
Sensor triggers
Threshold triggers
Activate when sensor crosses threshold:
// Activate when distance drops below 30cm
trigger on sensors.distance.front < 30 {
activate("ObstacleStop")
}
// Deactivate when safe
trigger on sensors.distance.front > 50 {
deactivate("ObstacleStop")
}
Range triggers
Activate when sensor is within range:
trigger on sensors.distance.front in 20..40 {
slow_down()
}
Multi-sensor triggers
Combine multiple sensor conditions:
trigger on (sensors.distance.front < 30 &&
sensors.distance.left < 30) {
// Trapped in corner
backup_and_turn()
}
Sensor change triggers
Activate on value change:
// Trigger on any change
trigger on change(sensors.line.center) {
log("Line detection changed")
}
// Trigger when value increases
trigger on rising(sensors.light) {
log("Light increasing")
}
// Trigger when value decreases
trigger on falling(battery.level) {
if battery.level < 20 {
activate("ReturnToBase")
}
}
Time triggers
Delay trigger
Execute after delay:
trigger after 5000 {
// Runs 5 seconds after Plan starts
log("Warmup complete")
}
Interval trigger
Execute repeatedly:
trigger every 1000 {
// Runs every second
log("Status check")
check_sensors()
}
Time-of-day trigger
Execute at specific times:
trigger at time.hour == 22 {
// Run at 10 PM
activate("NightMode")
}
trigger at time.hour == 6 {
// Run at 6 AM
deactivate("NightMode")
activate("DayMode")
}
Schedule trigger
Cron-style scheduling:
// Every hour
trigger schedule "0 * * * *" {
upload_status()
}
// Every day at midnight
trigger schedule "0 0 * * *" {
reset_daily_stats()
}
// Weekdays at 9 AM
trigger schedule "0 9 * * 1-5" {
start_work_routine()
}
State triggers
Plan state triggers
React to Plan state changes:
trigger on plan_activated("EmergencyStop") {
// Stop all other activities
deactivate_group("normal_ops")
}
trigger on plan_deactivated("EmergencyStop") {
// Resume normal operations
activate_group("normal_ops")
}
Variable state triggers
React to variable changes:
var mode = "idle"
trigger on mode == "active" {
log("Entering active mode")
start_motors()
}
trigger on mode == "idle" {
log("Entering idle mode")
stop_motors()
}
System state triggers
React to system conditions:
trigger on battery.low {
activate("LowBatteryMode")
}
trigger on battery.charging {
log("Now charging")
}
trigger on connection_lost("RemoteControl") {
activate("AutonomousMode")
}
trigger on connection_restored("RemoteControl") {
deactivate("AutonomousMode")
}
Location triggers
Position triggers
React to location:
trigger on position.x > 100 {
log("Crossed X=100 boundary")
}
trigger on distance_to(home_position) < 10 {
log("Arrived home")
}
Zone triggers
React to entering/leaving zones:
var danger_zone = {x: 50, y: 50, radius: 20}
trigger on entering_zone(danger_zone) {
log("Warning: Entering danger zone")
slow_down()
}
trigger on leaving_zone(danger_zone) {
log("Left danger zone")
resume_speed()
}
Geofence triggers
var boundary = [
{x: 0, y: 0},
{x: 100, y: 0},
{x: 100, y: 100},
{x: 0, y: 100}
]
trigger on outside_boundary(boundary) {
log("Outside operating area!")
navigate_inside()
}
Event handlers
Message events
Handle incoming messages:
on_message("command") { data ->
log("Received command: " + data.action)
execute_command(data)
}
on_message("emergency") { _ ->
emergency_stop()
}
Filtered message events
Handle specific message content:
on_message("command") where data.action == "stop" {
stop()
}
on_message("command") where data.action == "go" {
drive(50, 50)
}
Broadcast events
Handle broadcast messages:
on_broadcast("status_request") { sender ->
send_to(sender, "status_response", get_status())
}
Hardware events
Sensor events
// When specific sensor triggers
on_sensor("bump.front") { triggered ->
if triggered {
backup()
}
}
// When sensor error occurs
on_sensor_error("distance.front") { error ->
log_error("Front sensor error: " + error.message)
use_backup_sensor()
}
Hardware connection events
on_hardware_connected() {
log("Hardware connected")
calibrate_sensors()
}
on_hardware_disconnected() {
log_error("Hardware lost!")
activate("SafeMode")
}
Button events
on_button_press("panic") {
emergency_stop()
}
on_button_release("hold") {
resume()
}
Conditional expressions
Comparison operators
| Operator | Description | Example |
|---|---|---|
== |
Equal | state == "active" |
!= |
Not equal | state != "error" |
< |
Less than | speed < 50 |
> |
Greater than | speed > 50 |
<= |
Less or equal | battery <= 20 |
>= |
Greater or equal | battery >= 80 |
Logical operators
| Operator | Description | Example |
|---|---|---|
&& |
AND | a > 0 && b > 0 |
|| |
OR | error || warning |
! |
NOT | !active |
Range checks
// Value in range
if value in 10..50 {
// value is between 10 and 50
}
// Value not in range
if value not in 10..50 {
// value is outside 10-50
}
Type checks
if typeof(value) == "number" {
process_number(value)
}
if typeof(value) == "string" {
process_string(value)
}
Null checks
// Check for null
if value == null {
value = default_value
}
// Null coalescing
var safe_value = value ?? default_value
Pattern matching
Match expression
var result = match state {
"idle" -> 0
"moving" -> 1
"paused" -> 2
_ -> -1 // Default case
}
Match with guards
match sensor_reading {
x where x < 0 -> log("Invalid reading")
x where x < 30 -> activate("TooClose")
x where x < 60 -> log("Caution range")
_ -> log("Safe distance")
}
Match on type
match incoming_data {
{type: "command", action: a} -> execute(a)
{type: "status"} -> send_status()
[head, ...tail] -> process_list(head, tail)
_ -> log("Unknown data format")
}
Wait conditions
wait_for
Block until condition is true:
// Wait for sensor
wait_for(sensors.distance.front < 30)
// Wait with timeout
wait_for(sensors.distance.front < 30, 5000) // 5 second timeout
wait_for_any
Wait for any condition:
var which = wait_for_any([
() -> sensors.distance.front < 30,
() -> sensors.bump.front,
() -> battery.low
])
match which {
0 -> handle_distance()
1 -> handle_bump()
2 -> handle_battery()
}
wait_for_all
Wait for all conditions:
wait_for_all([
() -> sensors.calibrated,
() -> motors.ready,
() -> gps.fix
])
log("All systems ready")
Debouncing and filtering
Debounce
Prevent rapid triggering:
// Require condition to be stable for 500ms
trigger on debounce(sensors.bump.front, 500) {
// Only triggers if bump stays true for 500ms
handle_bump()
}
Rate limiting
Limit trigger frequency:
// Max once per second
trigger on rate_limit(sensors.distance.front < 30, 1000) {
log("Obstacle detected")
}
Smoothing
Filter noisy sensors:
// Moving average of last 10 readings
var smooth_distance = smooth(sensors.distance.front, 10)
trigger on smooth_distance < 30 {
// Only trigger on stable readings
stop()
}
Compound conditions
All conditions
var all_clear = all([
sensors.distance.front > 50,
sensors.distance.left > 30,
sensors.distance.right > 30
])
if all_clear {
drive_forward()
}
Any condition
var obstacle = any([
sensors.distance.front < 30,
sensors.bump.front,
sensors.bump.left,
sensors.bump.right
])
if obstacle {
stop()
}
None condition
var safe = none([
sensors.bump.front,
sensors.bump.left,
sensors.bump.right,
battery.low
])
if safe {
continue_operation()
}
Conditional plan activation
Auto-activate plans
// Define auto-activation rules
auto_activate("ObstacleStop") when sensors.distance.front < 20
auto_activate("LowBattery") when battery.level < 15
auto_activate("NightMode") when time.hour >= 22 || time.hour < 6
// With priority
auto_activate("Emergency", {priority: 100}) when sensors.bump.front
Conditional plan groups
// Define conditional groups
condition_group "autonomous_conditions" {
sensors.distance.front > 30,
!sensors.bump.front,
battery.level > 10
}
// Check group
if check_conditions("autonomous_conditions") {
activate("AutonomousPlan")
} else {
activate("SafeMode")
}
Best practices
Ordered conditions
Check most important conditions first:
loop {
// Safety first
if sensors.bump.front || sensors.distance.front < 10 {
emergency_stop()
continue
}
// Battery second
if battery.low {
activate("ReturnToBase")
continue
}
// Normal operation
do_normal_work()
delay(50)
}
Hysteresis
Prevent oscillation:
var THRESHOLD_LOW = 25
var THRESHOLD_HIGH = 35
var is_close = false
loop {
var dist = sensors.distance.front
// Different thresholds for on/off
if !is_close && dist < THRESHOLD_LOW {
is_close = true
slow_down()
} else if is_close && dist > THRESHOLD_HIGH {
is_close = false
resume_speed()
}
delay(50)
}
Timeout handling
Always handle timeouts:
try {
var result = wait_for(condition, 5000)
process_result(result)
} catch error {
if error.code == "TIMEOUT" {
log_warn("Condition not met in time")
handle_timeout()
} else {
throw error
}
}