haply 1.3.1

Haply Robotics Client Library for the Inverse Service
Documentation
//! ============================================================================
//! EXPERIMENTAL / INTERNAL-ONLY EXAMPLE
//! ----------------------------------------------------------------------------
//! This SDF demo is an experimental feature intended for internal testing.
//! It is not part of the stable public surface and may change or break without
//! notice. Do not rely on it in production.
//! ============================================================================
//!
//! SDF haptic effects -- sphere or cube.
//!
//! Usage:
//!   cargo run --example sdf_demo                # default: sphere
//!   cargo run --example sdf_demo -- --sphere
//!   cargo run --example sdf_demo -- --cube
//!
//! Toggle `USE_HTTP` to switch between HTTP and WS configuration.

use haply::{
    device_model::{
        EasingType,
        Inverse3Configure,
        Linear3D,
        SdfHfxObject,
        SdfOutputConfigure,
        SdfPrimitive,
        SymmetryMode,
        TransformPatch,
    },
    http::InverseHttpClient,
    HaplyDevice,
};
use std::io::Write;
use std::{ env, 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 args: Vec<String> = env::args().skip(1).collect();
    let use_cube = args.iter().any(|a| a == "--cube");

    let (effect_id, shape, label) = if use_cube {
        (
            "cube1",
            SdfPrimitive::Box {
                b: Linear3D { x: 0.03, y: 0.03, z: 0.03 },
            },
            "Cube",
        )
    } else {
        ("sphere1", SdfPrimitive::Sphere { r: 0.05 }, "Sphere")
    };

    println!("WARNING: sdf_demo is experimental/internal-only and not a stable public API.");
    println!("SDF Demo: {} effect", label);

    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 sdf_obj = SdfHfxObject {
        id: effect_id.to_string(),
        shape: Some(shape),
        transform: Some(TransformPatch {
            position: Some(Linear3D { x: 0.0, y: -0.1, z: 0.18 }),
            rotation: None,
            scale: None,
        }),
        force_scale: Some(-2.0),
        range: Some(0.02),
        ease: Some(EasingType::CubicInOut),
        reverse_easing: Some(false),
        symmetry: Some(SymmetryMode::Single),
    };

    if USE_HTTP {
        println!("Creating {} SDF via HTTP...", label);
        http.set_sdf_by_id("inverse3", &id, effect_id, Some(SESSION), &sdf_obj).await?;
    } else {
        println!("Enabling SDF output via WS...");
        device.configure_inverse3(
            &id,
            Inverse3Configure {
                sdf: Some(SdfOutputConfigure { state_output: true }),
                ..Default::default()
            },
            Some(true)
        ).await?;

        println!("Creating {} SDF via WS...", label);
        device.sdf_set(&id, vec![sdf_obj], Some("application".to_string()), Some(true)).await?;
    }

    println!("SDF active -- streaming state (Ctrl+C to stop)");
    let mut ticker = interval(Duration::from_millis(10));
    loop {
        ticker.tick().await;

        // Ping to keep the session active and state flowing
        device.send_ping().await?;

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

        if let Some(inv) = state.inverse3.first() {
            let pos = inv.state.cursor_position.unwrap_or_default();
            let force = inv.state.current_cursor_force.unwrap_or_default();

            print!(
                "\rPos: ({:.4},{:.4},{:.4}) Force: ({:.4},{:.4},{:.4})   ",
                pos.x,
                pos.y,
                pos.z,
                force.x,
                force.y,
                force.z
            );
            std::io::stdout().flush().unwrap();
        }
    }
}