Skip to main content

NodeImpl

Trait NodeImpl 

Source
pub trait NodeImpl:
    Send
    + Sync
    + Debug
    + 'static {
    type Config: DeserializeOwned + Send + Sync + Debug;

    const NODE_TYPE: Option<NodeType> = None;
    const NODE_TAGS: &'static [NodeTag] = _;

    // Required methods
    fn init<'async_trait>(
        config: Self::Config,
    ) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>
       where Self: Sized + 'async_trait;
    fn run<'life0, 'life1, 'async_trait>(
        &'life0 mut self,
        ctx: &'life1 Context,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;

    // Provided methods
    fn shutdown<'life0, 'async_trait>(
        &'life0 mut self,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait { ... }
    fn health_check<'life0, 'async_trait>(
        &'life0 self,
    ) -> Pin<Box<dyn Future<Output = HealthStatus> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait { ... }
    fn preflight(&self) -> Result<()> { ... }
}
Expand description

Core trait that all Mecha10 nodes must implement.

This trait defines the lifecycle of a node:

  1. init() - Initialize the node with configuration
  2. run() - Main execution loop
  3. shutdown() - Graceful shutdown
  4. health_check() - Report health status

§Example

use mecha10_core::prelude::*;

#[derive(Debug)]
struct MyNode {
    config: MyConfig,
}

#[derive(Debug, Deserialize)]
struct MyConfig {
    rate: f32,
}

#[async_trait]
impl NodeImpl for MyNode {
    type Config = MyConfig;

    async fn init(config: Self::Config) -> Result<Self> {
        info!("Initializing node with rate: {}", config.rate);
        Ok(Self { config })
    }

    async fn run(&mut self, ctx: &Context) -> Result<()> {
        let mut images = ctx.subscribe(sensor::CAMERA_RGB).await?;

        while let Some(image) = images.recv().await {
            // Process image
            info!("Received image: {}x{}", image.width, image.height);
        }

        Ok(())
    }
}

Provided Associated Constants§

Source

const NODE_TYPE: Option<NodeType> = None

Built-in node type, if this is one of the framework-provided node kinds. None for custom/project-local nodes – NodeType is deliberately closed (it drives 1:1 config-schema resolution in mecha10-schema-registry), so a custom node correctly stays None here rather than being forced to misrepresent itself as one of the built-in variants. This matches mecha10-schema-registry::resolve_node_type’s existing None -> generic-schema-fallback behavior for anything unrecognized.

Source

const NODE_TAGS: &'static [NodeTag] = _

Capability tags. Empty by default. Unlike NODE_TYPE, custom/project-local nodes are encouraged to set this – tagging e.g. a custom lidar node NodeTag::Sensor lets it be picked up by any dashboard widget that requires that tag, without needing an entry in mecha10-schema-registry at all.

Required Associated Types§

Source

type Config: DeserializeOwned + Send + Sync + Debug

Configuration type for this node

Required Methods§

Source

fn init<'async_trait>( config: Self::Config, ) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>
where Self: Sized + 'async_trait,

Initialize the node with the given configuration.

This is called once when the node starts. Use this to:

  • Load configuration
  • Initialize resources (models, connections, etc.)
  • Validate prerequisites
§Errors

Return an error if initialization fails. The node will not start.

Source

fn run<'life0, 'life1, 'async_trait>( &'life0 mut self, ctx: &'life1 Context, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Main execution loop for the node.

This is where your node’s business logic lives. Typical patterns:

  • Subscribe to topics and process messages
  • Run periodic tasks
  • Publish results

This method should run indefinitely until:

  • The context is cancelled (shutdown requested)
  • An unrecoverable error occurs
§Errors

Return an error if the node encounters an unrecoverable error. The node will transition to Error state and shutdown.

Provided Methods§

Source

fn shutdown<'life0, 'async_trait>( &'life0 mut self, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Graceful shutdown hook.

Called when the node is shutting down. Use this to:

  • Close connections
  • Flush buffers
  • Save state
  • Release resources

The default implementation does nothing.

§Errors

Errors during shutdown are logged but don’t prevent shutdown.

Source

fn health_check<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = HealthStatus> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Health check for monitoring.

Called periodically to check node health. Use this to:

  • Report current state
  • Check resource usage
  • Validate connections

The default implementation returns healthy status.

Source

fn preflight(&self) -> Result<()>

Node-specific pre-run content check.

This is the “prerun” layer that catches a bad node config before the node’s main loop starts, rather than crashing deep inside run() – e.g. a camera device path that doesn’t exist, a model file that was never downloaded, or two GPIO pins wired to the same physical pin.

Called once, after init() has already produced a fully-typed (and, per crate::config::Validate, schema/shape-validated) Self, and before run() starts. Unlike Validate::validate() – which only sees the raw deserialized config and runs during config loading – preflight() runs on the initialized node, so it can also check state derived during init() (e.g. a resolved file path) rather than just raw config fields.

The default implementation does nothing, so opting in is non-breaking for every existing NodeImpl.

§Errors

Return an error with an actionable message (see node-runner’s preflight.rs for the established convention: a ❌ header naming what failed, followed by numbered remediation steps) if the node should not start. The node will not run.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§