Plan Commands

Managing and controlling Plans

Plan Commands

Reference for commands that manage Plan lifecycle, activation, and coordination.

Pro Tip

Plans can coordinate with each other using signals and groups. This lets you build complex behaviors from simple building blocks - for example, a "Patrol" plan that activates "ObstacleAvoid" when needed, then resumes when the coast is clear.


Plan lifecycle

Plan Lifecycle


Activation commands

activate

Start a Plan.

activate(plan_name)
activate(plan_name, options)
Parameter Type Description
plan_name string Name of Plan to activate
options object Optional configuration

Options:

Option Type Default Description
priority number 0 Execution priority
exclusive boolean false Stop conflicting Plans
params object {} Parameters to pass
// Simple activation
activate("PatrolPlan")

// With options
activate("EmergencyStop", {
    priority: 100,
    exclusive: true
})

// Pass parameters
activate("NavigateTo", {
    params: {x: 100, y: 200}
})

deactivate

Stop a Plan.

deactivate(plan_name)
deactivate()  // Stop current Plan
// Stop specific Plan
deactivate("PatrolPlan")

// Stop self
deactivate()

pause

Temporarily suspend a Plan.

pause(plan_name)
pause()  // Pause current Plan
// Pause for emergency check
pause("PatrolPlan")
check_emergency()
resume("PatrolPlan")

resume

Continue a paused Plan.

resume(plan_name)
resume("PatrolPlan")

Plan state commands

is_active

Check if a Plan is running.

is_active(plan_name) -> boolean
if is_active("PatrolPlan") {
    log("Patrol is running")
}

is_paused

Check if a Plan is paused.

is_paused(plan_name) -> boolean
if is_paused("PatrolPlan") {
    resume("PatrolPlan")
}

get_state

Get detailed Plan state.

get_state(plan_name) -> object

Returns:

{
    name: "PatrolPlan",
    state: "running",      // "inactive", "running", "paused"
    started_at: 1234567890,
    run_time: 60000,       // milliseconds
    loop_count: 1200,
    errors: 0
}

list_plans

Get all available Plans.

list_plans() -> array
var plans = list_plans()
for plan in plans {
    log(plan.name + ": " + plan.state)
}

list_active

Get currently running Plans.

list_active() -> array
var active = list_active()
log("Running: " + length(active) + " plans")

Priority and conflicts

Plan priority

Higher priority Plans execute first in each cycle:

// Emergency Stop has highest priority
activate("EmergencyStop", {priority: 100})

// Normal operations
activate("PatrolPlan", {priority: 10})

// Background tasks
activate("DataLogger", {priority: 1})

Exclusive mode

Exclusive Plans stop conflicting Plans:

// Stop all motor-controlling Plans
activate("ManualControl", {
    exclusive: true,
    conflicts: ["PatrolPlan", "NavigateTo", "ObstacleAvoid"]
})

Conflict resolution

plan ResourceManager {
    var motor_owner = null

    function request_motors(plan_name) {
        if motor_owner != null && motor_owner != plan_name {
            return false  // Motors busy
        }
        motor_owner = plan_name
        return true
    }

    function release_motors(plan_name) {
        if motor_owner == plan_name {
            motor_owner = null
        }
    }
}

Parameter passing

Pass parameters on activation

activate("NavigateTo", {
    params: {
        target_x: 100,
        target_y: 200,
        speed: 50
    }
})

Receive parameters

plan NavigateTo {
    var target_x = params.target_x ?? 0
    var target_y = params.target_y ?? 0
    var speed = params.speed ?? 30

    init {
        log("Navigating to " + target_x + ", " + target_y)
    }

    loop {
        move_toward(target_x, target_y, speed)
        if at_position(target_x, target_y) {
            deactivate()
        }
        delay(50)
    }
}

Default parameter values

plan Configurable {
    // Use ?? operator for defaults
    var threshold = params.threshold ?? 30
    var speed = params.speed ?? 50
    var enabled = params.enabled ?? true
}

Plan communication

Signals between plans

// Send signal to another Plan
signal(plan_name, signal_name, data)
// Sender Plan
plan Scout {
    loop {
        if sensors.distance.front < 20 {
            signal("Worker", "obstacle_found", {
                x: position.x,
                y: position.y
            })
        }
        delay(100)
    }
}

// Receiver Plan
plan Worker {
    on_signal("obstacle_found") { data ->
        log("Obstacle at: " + data.x + ", " + data.y)
        avoid_location(data.x, data.y)
    }

    loop {
        do_work()
        delay(100)
    }
}

Wait for signal

// Block until signal received
wait_for_signal(signal_name)
wait_for_signal(signal_name, timeout_ms)
plan Synchronized {
    init {
        log("Waiting for start signal...")
        wait_for_signal("start")
        log("Starting!")
    }

    loop {
        do_work()
        delay(50)
    }
}

Plan groups

Create plan group

create_group(group_name, plan_names)
create_group("patrol_team", ["Scout", "Worker1", "Worker2"])

Activate group

activate_group(group_name)
// Start all patrol plans together
activate_group("patrol_team")

Deactivate group

deactivate_group(group_name)
// Stop all patrol plans
deactivate_group("patrol_team")

Group operations

// Signal all plans in group
signal_group("patrol_team", "return_home", {})

// Check if all plans in group are active
var all_active = group_all_active("patrol_team")

// Check if any plan in group is active
var any_active = group_any_active("patrol_team")

Scheduled activation

activate_at

Activate Plan at specific time.

activate_at(plan_name, timestamp)
// Activate at specific time
var patrol_time = now() + 3600000  // 1 hour from now
activate_at("NightPatrol", patrol_time)

activate_after

Activate Plan after delay.

activate_after(plan_name, delay_ms)
// Activate after 5 minutes
activate_after("DataUpload", 300000)

Schedule recurring

schedule(plan_name, interval_ms)
schedule(plan_name, cron_expression)
// Run every hour
schedule("StatusReport", 3600000)

// Run at specific times (cron-like)
schedule("NightPatrol", "0 22 * * *")  // 10 PM daily

Plan events

Lifecycle events

plan EventDemo {
    on_plan_event("activated") { plan_name ->
        log("Plan activated: " + plan_name)
    }

    on_plan_event("deactivated") { plan_name ->
        log("Plan deactivated: " + plan_name)
    }

    on_plan_event("error") { error ->
        log_error("Plan error: " + error.plan + " - " + error.message)
    }
}

Subscribe to plan changes

// Watch specific Plan
on_plan_state_change("PatrolPlan") { old_state, new_state ->
    log("Patrol: " + old_state + " -> " + new_state)
}

Error handling in plans

Plan-level error handler

plan RobustPlan {
    on_error { error ->
        log_error("Plan error: " + error.message)
        stop()  // Safe state
        // Optionally restart
        delay(1000)
        activate("RobustPlan")
    }

    loop {
        risky_operation()
        delay(50)
    }
}

Restart on failure

plan AutoRestart {
    const MAX_RESTARTS = 3
    var restart_count = params.restart_count ?? 0

    on_error { error ->
        if restart_count < MAX_RESTARTS {
            log_warn("Restarting after error: " + error.message)
            activate("AutoRestart", {
                params: {restart_count: restart_count + 1}
            })
        } else {
            log_error("Max restarts reached, stopping")
        }
    }

    init {
        if restart_count > 0 {
            log("Restart #" + restart_count)
        }
    }

    loop {
        do_work()
        delay(50)
    }
}

Plan metadata access

Get plan info

var info = plan_info("PatrolPlan")
// Returns:
// {
//     name: "PatrolPlan",
//     version: "1.0.0",
//     author: "RobotBuilder",
//     permissions: ["sensors.read", "actuators.motors"],
//     description: "Autonomous patrol behavior"
// }

Check permissions

var has_motors = plan_has_permission("PatrolPlan", "actuators.motors")
if !has_motors {
    log_warn("PatrolPlan cannot control motors")
}

List required permissions

var perms = plan_permissions("PatrolPlan")
for perm in perms {
    log("Requires: " + perm)
}

Best practices

Clean activation

function safe_activate(plan_name) {
    // Check if already active
    if is_active(plan_name) {
        log("Plan already active: " + plan_name)
        return false
    }

    // Activate
    activate(plan_name)

    // Verify
    if !is_active(plan_name) {
        log_error("Failed to activate: " + plan_name)
        return false
    }

    return true
}

Clean deactivation

function safe_deactivate(plan_name) {
    if is_active(plan_name) {
        deactivate(plan_name)

        // Wait for cleanup
        var timeout = 5000
        var start = now()
        while is_active(plan_name) && now() - start < timeout {
            delay(100)
        }

        if is_active(plan_name) {
            log_error("Plan did not stop: " + plan_name)
            return false
        }
    }
    return true
}

Plan handoff

function handoff(from_plan, to_plan, state_data) {
    // Save state
    global.handoff_state = state_data

    // Deactivate old plan
    deactivate(from_plan)

    // Activate new plan with state
    activate(to_plan, {
        params: {handoff_state: state_data}
    })
}

// Usage
handoff("SearchMode", "TrackMode", {
    target_x: 100,
    target_y: 200
})

In this section
Topics
reference plans commands lifecycle
See also