| Crates.io | mecha10-runtime |
| lib.rs | mecha10-runtime |
| version | 0.1.23 |
| created_at | 2025-11-25 02:20:57.011986+00 |
| updated_at | 2025-12-30 22:59:21.00629+00 |
| description | Runtime supervisor for Mecha10 nodes - launching, monitoring, and lifecycle management |
| homepage | |
| repository | https://github.com/mecha10/mecha10 |
| max_upload_size | |
| id | 1949090 |
| size | 101,038 |
Runtime supervisor for Mecha10 nodes - launching, monitoring, and lifecycle management.
[dependencies]
mecha10-runtime = "0.1.0"
async-trait = "0.1"
anyhow = "1.0"
use mecha10_runtime::prelude::*;
use anyhow::Result;
struct CameraNode {
frame_count: u64,
}
#[async_trait]
impl NodeRunner for CameraNode {
fn name(&self) -> &str {
"camera"
}
async fn run(&mut self) -> Result<()> {
loop {
self.frame_count += 1;
tokio::time::sleep(tokio::time::Duration::from_millis(33)).await;
}
}
async fn health_check(&self) -> HealthStatus {
if self.frame_count > 0 {
HealthStatus::Healthy
} else {
HealthStatus::Unhealthy {
reason: "No frames captured".to_string()
}
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
let mut runtime = Runtime::builder()
.log_level("info")
.build();
runtime.run_node("camera", Box::new(CameraNode { frame_count: 0 })).await?;
Ok(())
}
use mecha10_runtime::prelude::*;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<()> {
let mut runtime = Runtime::builder()
.log_level("info")
.restart_policy(RestartPolicy::OnFailure {
max_retries: 3,
backoff: Duration::from_secs(1),
})
.health_check_interval(Duration::from_secs(30))
.shutdown_timeout(Duration::from_secs(10))
.build();
let nodes: Vec<Box<dyn NodeRunner>> = vec![
Box::new(CameraNode { /* ... */ }),
Box::new(MotorNode { /* ... */ }),
Box::new(PlannerNode { /* ... */ }),
];
runtime.run_nodes(nodes).await?;
Ok(())
}
use mecha10_runtime::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
let mut runtime = Runtime::builder()
.with_launcher(true)
.build();
let launcher = runtime.launcher().unwrap();
// Register node types
launcher.register("camera".to_string(), || {
Box::new(CameraNode { frame_count: 0 })
}).await;
launcher.register("motor".to_string(), || {
Box::new(MotorNode::new())
}).await;
// Launch nodes dynamically
launcher.launch("camera").await?;
launcher.launch("motor").await?;
// Run with launcher service
runtime.run_with_launcher().await?;
Ok(())
}
// Pretty format (human-readable, colored)
Runtime::builder()
.log_format(LogFormat::Pretty)
.build();
// JSON format (structured logs for parsing)
Runtime::builder()
.log_format(LogFormat::Json)
.build();
// Compact format (minimal output)
Runtime::builder()
.log_format(LogFormat::Compact)
.build();
// Never restart failed nodes
Runtime::builder()
.restart_policy(RestartPolicy::Never)
.build();
// Restart on failure with exponential backoff
Runtime::builder()
.restart_policy(RestartPolicy::OnFailure {
max_retries: 5,
backoff: Duration::from_secs(1), // 1s, 2s, 4s, 8s, 16s
})
.build();
// Always restart with constant backoff
Runtime::builder()
.restart_policy(RestartPolicy::Always {
backoff: Duration::from_secs(5),
})
.build();
Implement the NodeRunner trait for each node type:
#[async_trait]
pub trait NodeRunner: Send + Sync {
/// Get the node name
fn name(&self) -> &str;
/// Run the node (main logic)
async fn run(&mut self) -> Result<()>;
/// Perform a health check (default: Healthy)
async fn health_check(&self) -> HealthStatus {
HealthStatus::Healthy
}
/// Handle shutdown signal (default: no-op)
async fn shutdown(&mut self) -> Result<()> {
Ok(())
}
}
// Custom health check logic
async fn health_check(&self) -> HealthStatus {
if self.is_connected() {
HealthStatus::Healthy
} else if self.reconnecting() {
HealthStatus::Degraded {
reason: "Reconnecting to sensor".to_string()
}
} else {
HealthStatus::Unhealthy {
reason: "Sensor disconnected".to_string()
}
}
}
// Access health checker from runtime
let health_checker = runtime.health_checker();
let all_statuses = health_checker.check_all().await;
for (node_name, status) in all_statuses {
println!("{}: {:?}", node_name, status);
}
The runtime automatically handles shutdown signals:
// Runs until Ctrl+C or SIGTERM
runtime.run_nodes(nodes).await?;
// Custom shutdown timeout
Runtime::builder()
.shutdown_timeout(Duration::from_secs(30))
.build();
// Custom shutdown logic in nodes
async fn shutdown(&mut self) -> Result<()> {
self.save_state().await?;
self.close_connections().await?;
Ok(())
}
Runtime
├── Supervisor (node lifecycle management)
│ ├── Launch nodes
│ ├── Monitor health
│ ├── Handle restarts
│ └── Coordinate shutdown
├── HealthChecker (health monitoring)
│ ├── Register nodes
│ ├── Track status
│ └── Report health
├── Launcher (dynamic node management) [optional]
│ ├── Register node factories
│ ├── Launch on demand
│ └── Stop running nodes
└── ShutdownHandle (graceful shutdown)
├── Signal handling
└── Broadcast to nodes
See the rover-robot example for a complete demonstration of using mecha10-runtime.
MIT