use clap::Parser;
use moteus::command::{PositionCommand, PositionFormat};
use moteus::query::QueryFormat;
use moteus::transport::args::TransportArgs;
use moteus::transport::async_transport::AsyncRouter;
use moteus::transport::transaction::Request;
use moteus::{Controller, Resolution};
use std::io::Write;
use std::time::Instant;
const STATUS_PERIOD_S: f64 = 0.1;
#[derive(Parser)]
#[command(about = "Measure maximal bandwidth for a position/query loop")]
struct Args {
#[arg(short = 't', long = "target", action = clap::ArgAction::Append)]
target: Vec<u8>,
#[arg(long)]
minimal_format: bool,
#[command(flatten)]
transport: TransportArgs,
}
#[tokio::main]
async fn main() -> Result<(), moteus::Error> {
let args = Args::parse();
let opts = args.transport.into();
let mut transport = AsyncRouter::with_options(&opts).await?;
let targets: Vec<u8> = if args.target.is_empty() {
let devices = transport.discover(0, 0).await?;
devices.iter().map(|d| d.can_id).collect()
} else {
args.target
};
if targets.is_empty() {
println!("No devices found!");
return Ok(());
}
println!("Testing with {} target(s):", targets.len());
for t in &targets {
println!(" * {}", t);
}
println!();
let controllers: Vec<Controller> = targets
.iter()
.map(|&id| {
let mut ctrl = Controller::new(id);
if args.minimal_format {
let mut pos_fmt = PositionFormat::default();
pos_fmt.position = Resolution::Int16;
pos_fmt.velocity = Resolution::Int16;
let mut qry_fmt = QueryFormat::default();
qry_fmt.mode = Resolution::Int16;
qry_fmt.position = Resolution::Int16;
qry_fmt.velocity = Resolution::Int16;
qry_fmt.torque = Resolution::Int16;
ctrl = ctrl.position_format(pos_fmt).query_format(qry_fmt);
}
ctrl
})
.collect();
let mut hz_count: u64 = 0;
let mut status_time = Instant::now();
loop {
hz_count += 1;
let mut requests: Vec<_> = controllers
.iter()
.map(|c| {
Request::new(
c.make_position_command(
&PositionCommand::new().position(f32::NAN).velocity(0.0),
true,
)
.into_frame(),
)
})
.collect();
transport.cycle(&mut requests).await?;
let count: usize = requests.iter().map(|r| r.responses.len()).sum();
let elapsed = status_time.elapsed().as_secs_f64();
if elapsed > STATUS_PERIOD_S {
let hz = hz_count as f64 / elapsed;
print!("{:6.1}Hz rx_count={} \r", hz, count);
std::io::stdout().flush().ok();
hz_count = 0;
status_time = Instant::now();
}
tokio::task::yield_now().await;
}
}