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

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
    }
}