wireband-edge 0.4.1

Lightweight Wire.Band client — semantic data middleware for any domain (IoT, AI/ML, DeFi, legal, geospatial, supply chain, and more)
Documentation
//! wireband-edge CLI
//!
//! Run a Wire.Band edge gateway from the command line.
//!
//! # Examples
//!
//!     wireband-edge --broker mqtt://localhost:1883 --topics "sensors/#" "machines/#"
//!
//!     wireband-edge \
//!         --broker mqtts://broker.example.com:8883 \
//!         --topics "factory/line1/#" \
//!         --backend http://my-server:8000 \
//!         --device-id factory-rpi4 \
//!         --delta-threshold 0.02

use std::time::Duration;

use clap::Parser;
use tracing::info;
use tracing_subscriber::{EnvFilter, fmt};

use wireband_edge::client::{ClientConfig, WireBandClient};

// ---------------------------------------------------------------------------
// CLI args
// ---------------------------------------------------------------------------

#[derive(Parser, Debug)]
#[command(
    name = "wireband-edge",
    version,
    about = "Wire.Band edge gateway — bridge MQTT to a Wire.Band backend"
)]
struct Args {
    /// MQTT broker URL  (mqtt:// or mqtts://)
    #[arg(short, long, default_value = "mqtt://localhost:1883")]
    broker: String,

    /// MQTT topic filters
    #[arg(short, long, num_args = 1.., default_value = "#")]
    topics: Vec<String>,

    /// Wire.Band backend HTTP URL
    #[arg(long, default_value = "http://localhost:8000")]
    backend: String,

    /// Unique identifier for this gateway node
    #[arg(short, long, default_value = "edge-node")]
    device_id: String,

    /// Bearer token for backend authentication
    #[arg(long)]
    api_key: Option<String>,

    /// Suppress numeric readings with fractional change below this value
    /// (0.0 = forward all, 0.02 = suppress if <2% change)
    #[arg(long, default_value_t = 0.0)]
    delta_threshold: f64,

    /// Flush interval in seconds
    #[arg(long, default_value_t = 1.0)]
    flush_interval: f64,

    /// Max events per HTTP flush request
    #[arg(long, default_value_t = 200)]
    flush_batch: usize,

    /// Local ring buffer capacity in events
    #[arg(long, default_value_t = 50_000)]
    buffer_size: usize,

    /// Max retry attempts per flush before backing off
    #[arg(long, default_value_t = 3)]
    max_retries: u32,

    /// Log level
    #[arg(long, default_value = "info")]
    log_level: String,
}

// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let args = Args::parse();

    // Tracing — WIREBAND_LOG env var overrides --log-level
    let filter = EnvFilter::try_from_env("WIREBAND_LOG")
        .unwrap_or_else(|_| EnvFilter::new(&args.log_level));
    fmt().with_env_filter(filter).init();

    let config = ClientConfig {
        backend_url:     args.backend.clone(),
        device_id:       args.device_id.clone(),
        api_key:         args.api_key.clone(),
        buffer_size:     args.buffer_size,
        flush_interval:  Duration::from_secs_f64(args.flush_interval),
        flush_batch:     args.flush_batch,
        max_retries:     args.max_retries,
        delta_threshold: args.delta_threshold,
        ..Default::default()
    };

    let client = WireBandClient::new(config);
    client.start();

    info!(
        device_id  = %args.device_id,
        broker     = %args.broker,
        topics     = ?args.topics,
        backend    = %args.backend,
        delta      = args.delta_threshold,
        "Wire.Band edge starting"
    );

    #[cfg(feature = "mqtt")]
    {
        use wireband_edge::mqtt::MqttConnector;

        let result = MqttConnector::new(&args.broker, args.delta_threshold)
            .run(client.clone(), args.topics)
            .await;

        client.stop().await;

        if let Err(e) = result {
            eprintln!("MQTT error: {e}");
            std::process::exit(1);
        }
    }

    #[cfg(not(feature = "mqtt"))]
    {
        eprintln!(
            "No connector enabled. Rebuild with a connector feature, e.g.:\n\
             cargo build --features mqtt"
        );
        client.stop().await;
        std::process::exit(1);
    }

    Ok(())
}