haply 1.3.1

Haply Robotics Client Library for the Inverse Service
Documentation
//! Dynamic damping at 1000 Hz — varies the damping scalar via a 0.5 Hz sin wave.
//!
//! Usage:
//!   cargo run --example dynamic_damping_demo
//!
//! Requires Haply Inverse Service (v3.5+) with an Inverse3 device connected.
//! Press Ctrl+C to stop.

use haply::{ device_model::{ DampingConfig, Force, ForceInput }, HaplyDevice };
use std::io::Write;
use std::time::{ Duration, Instant };
use tokio::time::interval;

const HTTP_BASE: &str = "http://localhost:10001";
const WS_URL: &str = "ws://localhost:10001/";

/// Centre of the sin wave (N·s/m).
const DAMPING_BASE: f32 = 6.0;
/// Peak deviation either side of the centre (N·s/m).
const DAMPING_AMPLITUDE: f32 = 4.0;
/// Oscillation frequency (Hz) — full cycle every 2 s.
const DAMPING_FREQ_HZ: f64 = 0.5;

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

    tokio::time::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);
    println!(
        "Damping oscillates {:.1}{:.1} N·s/m at {:.1} Hz  (Ctrl+C to stop)",
        DAMPING_BASE - DAMPING_AMPLITUDE,
        DAMPING_BASE + DAMPING_AMPLITUDE,
        DAMPING_FREQ_HZ
    );

    // Send an initial zero force to engage the control loop before the tight loop starts.
    device.update_force(
        vec![ForceInput { device_id: id.clone(), forces: Force { x: 0.0, y: 0.0, z: 0.0 } }],
        Some(true),
        Some(true)
    ).await?;

    let start = Instant::now();
    let mut ticker = interval(Duration::from_millis(1)); // 1000 Hz
    let mut print_counter: u32 = 0;

    loop {
        ticker.tick().await;

        let t = start.elapsed().as_secs_f64();
        let scalar =
            DAMPING_BASE +
            DAMPING_AMPLITUDE * ((2.0 * std::f64::consts::PI * DAMPING_FREQ_HZ * t).sin() as f32);

        device.set_damping(&id, DampingConfig { scalar: Some(scalar), vector: None }, None).await?;

        // Zero force every tick to keep the control loop engaged.
        device.update_force(
            vec![ForceInput { device_id: id.clone(), forces: Force { x: 0.0, y: 0.0, z: 0.0 } }],
            Some(true),
            None
        ).await?;

        device.send_command().await?;

        // Print at ~10 Hz (every 100 ticks) to avoid terminal overhead.
        print_counter += 1;
        if print_counter >= 100 {
            print_counter = 0;
            let state = device.read_state().await?;
            if let Some(inv) = state.inverse3.first() {
                let vel = inv.state.cursor_velocity.unwrap_or_default();
                let speed = (vel.x * vel.x + vel.y * vel.y + vel.z * vel.z).sqrt();
                print!("\rdamping={:.3} N·s/m  speed={:.4} m/s  t={:.2}s   ", scalar, speed, t);
                std::io::stdout().flush().unwrap();
            }
        }
    }
}