# Behavior Interrupt System
Reusable system for nodes to interrupt and resume behavior tree execution.
## Overview
The behavior interrupt system provides a configurable, reusable way for any node to pause autonomous behaviors when issuing direct commands. This ensures that user commands take priority over autonomous behaviors.
## Key Components
### 1. BehaviorControl Message
Enhanced control message for behavior tree lifecycle:
```rust
pub struct BehaviorControl {
pub action: String, // "enable", "disable", "interrupt", "resume"
pub source: Option<String>, // Node that triggered the command
pub duration_secs: Option<u64>, // Auto-resume duration
pub timestamp: u64,
}
```
**Actions:**
- **`enable`**: Enable behavior tree (from dashboard)
- **`disable`**: Disable behavior tree (from dashboard)
- **`interrupt`**: Pause behavior tree (from node command)
- **`resume`**: Resume behavior tree (manual or auto)
### 2. BehaviorInterruptTrigger
Reusable trigger that nodes can embed to manage behavior interruption:
```rust
pub struct BehaviorInterruptTrigger {
source: String,
config: BehaviorInterruptConfig,
// ... internal state
}
```
**Features:**
- Automatic interrupt on command
- Configurable auto-resume with timeout
- Optional completion detection
- Source tracking
### 3. BehaviorInterruptConfig
Configuration for interrupt behavior:
```rust
pub struct BehaviorInterruptConfig {
pub enabled: bool, // Enable/disable interruption
pub mode: InterruptMode, // Interrupt behavior
pub timeout_secs: u64, // Auto-resume timeout
pub await_completion: bool, // Wait for completion
pub completion_topic: Option<String>, // Topic to monitor
pub control_topic: String, // Where to send commands
}
```
### 4. InterruptMode
```rust
pub enum InterruptMode {
Disabled, // No interruption
InterruptOnly, // Interrupt without auto-resume
InterruptWithAutoResume, // Interrupt with auto-resume
}
```
## Usage
### Basic Usage in a Node
```rust
use mecha10_core::behavior_interrupt::{BehaviorInterruptTrigger, BehaviorInterruptConfig};
use mecha10_core::prelude::*;
pub struct MyNode {
behavior_trigger: BehaviorInterruptTrigger,
// ... other fields
}
impl MyNode {
pub fn new(config: MyNodeConfig) -> Result<Self> {
// Create trigger from config
let behavior_trigger = BehaviorInterruptTrigger::new(
"my-node",
config.behavior_interrupt,
);
Ok(Self {
behavior_trigger,
// ... other fields
})
}
async fn send_motor_command(&mut self, ctx: &Context, cmd: MotorCommand) -> Result<()> {
// Interrupt behavior tree before sending command
self.behavior_trigger.interrupt(ctx).await?;
// Send motor command
ctx.publish(&cmd).await?;
// Optionally resume manually (or let auto-resume handle it)
// self.behavior_trigger.resume(ctx).await?;
Ok(())
}
}
```
### Configuration Example
```toml
[behavior_interrupt]
enabled = true
mode = "interrupt_with_auto_resume"
timeout_secs = 30
await_completion = false
control_topic = "/behavior/control"
```
### Interrupt Modes Explained
**Disabled**
```toml
mode = "disabled"
```
- Behavior tree is never interrupted
- Use when autonomous behaviors should have priority
**Interrupt Only**
```toml
mode = "interrupt_only"
```
- Pauses behavior tree when command is issued
- No automatic resumption
- Requires manual re-enable from dashboard
**Interrupt with Auto-Resume**
```toml
mode = "interrupt_with_auto_resume"
timeout_secs = 30
```
- Pauses behavior tree when command is issued
- Automatically resumes after timeout
- Ideal for seamless manual/autonomous switching
## Advanced Features
### Check Interrupt Status
```rust
if trigger.is_interrupted() {
println!("Behavior tree is currently interrupted");
if let Some(duration) = trigger.interrupt_duration() {
println!("Interrupted for {}s", duration.as_secs());
}
}
```
### Manual Resume
```rust
// Resume behavior tree immediately
trigger.resume(ctx).await?;
```
### Completion Detection (Future)
```toml
[behavior_interrupt]
await_completion = true
completion_topic = "/motor/status"
```
When implemented, the trigger will wait for a message on `completion_topic` before auto-resuming.
## Integration with Behavior Executor
The behavior-executor node listens to `/behavior/control` and handles these actions:
```rust
match control_msg.action.as_str() {
"enable" => /* Enable behavior tree */,
"disable" => /* Disable behavior tree */,
"interrupt" => {
// Pause execution
// Schedule auto-resume if duration_secs is set
},
"resume" => /* Resume behavior tree */,
}
```
## Example: LLM Command Node
The `llm-command` node uses this system to interrupt behaviors when issuing motor/navigation commands:
```rust
pub struct OpenAIReasoningNode {
llm: LlmNode,
behavior_trigger: BehaviorInterruptTrigger,
// ...
}
impl OpenAIReasoningNode {
async fn publish_action(&mut self, ctx: &Context, action: ParsedAction) -> Result<()> {
// Interrupt for motor/navigation commands
match &action {
ParsedAction::Navigate { .. } | ParsedAction::Motor { .. } => {
self.behavior_trigger.interrupt(ctx).await?;
}
_ => {}
}
// Publish the action
// ...
}
}
```
## Thread Safety
The `BehaviorInterruptTrigger` manages auto-resume tasks internally and cleans them up on drop. The auto-resume task is automatically cancelled when:
- A new interrupt is triggered
- The trigger is manually resumed
- The trigger is dropped
## Testing
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_default() {
let config = BehaviorInterruptConfig::default();
assert!(config.enabled);
assert_eq!(config.mode, InterruptMode::InterruptOnly);
assert_eq!(config.timeout_secs, 30);
}
#[test]
fn test_control_message_creation() {
let interrupt = BehaviorControl::interrupt("test-node");
assert_eq!(interrupt.action, "interrupt");
assert_eq!(interrupt.source, Some("test-node".to_string()));
let interrupt_with_duration = BehaviorControl::interrupt_with_duration("test-node", 60);
assert_eq!(interrupt_with_duration.duration_secs, Some(60));
}
}
```
## See Also
- [llm-command README](../../../nodes/llm-command/README.md) - Example usage
- [behavior-executor README](../../../nodes/behavior-executor/README.md) - Control message handling