haply 1.3.1

Haply Robotics Client Library for the Inverse Service
Documentation
//! Bubble navigation -- configures navigation and streams cursor + workspace position.
//!
//! Usage:
//!   cargo run --example navigation_demo
//!
//! Toggle `USE_HTTP` to switch between HTTP and WS configuration.

use haply::{
    device_model::{
        BubbleNavigationSettings,
        BubbleCenterSettings,
        Command,
        CollisionDetectionSettings,
        Inverse3Configure,
        Linear3D,
        NavigationConfigure,
        SdfPrimitive,
        TransformPatch,
    },
    http::InverseHttpClient,
    HaplyDevice,
};
use std::io::Write;
use std::time::Duration;
use tokio::time::{ interval, sleep };

const USE_HTTP: bool = false;
const HTTP_BASE: &str = "http://localhost:10001";
const WS_URL: &str = "ws://localhost:10001/";
/// Session selector for HTTP session-scoped endpoints.
const SESSION: &str = ":-1";

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let mut device = HaplyDevice::new(HTTP_BASE, WS_URL).await?;
    let http = InverseHttpClient::new(HTTP_BASE);

    sleep(Duration::from_millis(300)).await;
    let state = device.read_state().await?;

    let inv = state.inverse3.first().ok_or("No Inverse3 device connected")?;
    let id = inv.device_id.clone();
    println!("Using Inverse3 device: {}", id);

    let nav_config = NavigationConfigure {
        mode: "bubble".to_string(),
        bubble: Some(BubbleNavigationSettings {
            center: Some(BubbleCenterSettings {
                // Zero offset from current cursor at navigation start.
                position: Some(Linear3D { x: 0.0, y: -0.05, z: 0.18 }),
                // `true` means `position` is interpreted as cursor-relative offset.
                relative: Some(false),
                follow: Some(false),
                speed: None,
            }),
            shape: Some(SdfPrimitive::Sphere { r: 0.05 }),
            velocity_zone_width: Some(0.03),
            max_velocity: Some(0.5),
            spring_surface: Some(7.0),
            damping_surface: Some(0.7),
            collision_detection: Some(CollisionDetectionSettings {
                // Disabled by default for position-verification tests.
                enabled: Some(false),
                force_threshold: Some(1.0),
                inflate_ratio: Some(2.0),
                exit_ratio: Some(0.7),
            }),
            ..Default::default()
        }),
    };

    if USE_HTTP {
        println!("Configuring navigation via HTTP...");
        http.set_navigation("inverse3", &id, Some(SESSION), &nav_config).await?;
    } else {
        println!("Configuring navigation via WS...");
        device.configure_inverse3(
            &id,
            Inverse3Configure {
                navigation: Some(nav_config),
                ..Default::default()
            },
            Some(true)
        ).await?;
    }

    println!("Navigation active -- streaming position (Ctrl+C to stop)");
    let mut ticker = interval(Duration::from_millis(50));
    let mut tick_count: u32 = 0;
    loop {
        ticker.tick().await;
        tick_count += 1;

        // Send command each tick to keep streaming alive.
        // Request full state every ~500ms for navigation module fields.
        if tick_count % 10 == 0 {
            device.send_force_full_render().await?;
        } else {
            device.send_command().await?;
        }

        let state = device.read_state().await?;

        if let Some(inv) = state.inverse3.first() {
            let cursor = inv.state.cursor_position.unwrap_or_default();
            let ws_pos = inv.state.transform
                .as_ref()
                .map(|t| t.position)
                .unwrap_or_default();
            let nav_center = inv.state.navigation
                .as_ref()
                .and_then(|n| n.bubble.as_ref())
                .and_then(|b| b.center);
            let nav_mode = inv.status.navigation
                .as_ref()
                .and_then(|s| s.mode.as_deref())
                .unwrap_or("off");

            print!(
                "\rCursor: ({:.4},{:.4},{:.4})  WS: ({:.4},{:.4},{:.4})  Nav: {:?}  Mode: {}   ",
                cursor.x,
                cursor.y,
                cursor.z,
                ws_pos.x,
                ws_pos.y,
                ws_pos.z,
                nav_center,
                nav_mode
            );
            std::io::stdout().flush().unwrap();
        }
    }
}