use std::collections::{HashMap, HashSet};
use crate::models::command::JointState;
use crate::models::profile::{JointDefinition, RealWorldMargins};
use crate::models::verdict::CheckResult;
pub fn check_acceleration_limits(
joints: &[JointState],
previous_joints: Option<&[JointState]>,
definitions: &[JointDefinition],
delta_time: f64,
margins: Option<&RealWorldMargins>,
) -> CheckResult {
let Some(prev) = previous_joints else {
return CheckResult {
name: "acceleration_limits".to_string(),
category: "physics".to_string(),
passed: true,
details: "skipped on first command (no previous joint states)".to_string(),
};
};
if !delta_time.is_finite() || delta_time <= 0.0 {
return CheckResult {
name: "acceleration_limits".to_string(),
category: "physics".to_string(),
passed: false,
details: format!(
"delta_time {:.6} is non-positive; acceleration is undefined",
delta_time
),
};
}
let def_map: HashMap<&str, &JointDefinition> =
definitions.iter().map(|d| (d.name.as_str(), d)).collect();
let prev_map: HashMap<&str, &JointState> = prev.iter().map(|p| (p.name.as_str(), p)).collect();
let current_names: HashSet<&str> = joints.iter().map(|s| s.name.as_str()).collect();
let mut violations: Vec<String> = Vec::new();
for state in joints {
let Some(def) = def_map.get(state.name.as_str()) else {
violations.push(format!(
"'{}': unknown joint (no definition found)",
state.name
));
continue;
};
let Some(prev_state) = prev_map.get(state.name.as_str()) else {
violations.push(format!(
"'{}': no previous joint state (cannot compute acceleration)",
state.name
));
continue;
};
if !state.velocity.is_finite() || !prev_state.velocity.is_finite() {
violations.push(format!("'{}': velocity is NaN or infinite", state.name));
continue;
}
let margin_factor = 1.0 - margins.map(|m| m.acceleration_margin).unwrap_or(0.0);
let limit = def.max_acceleration * margin_factor;
let accel = (state.velocity - prev_state.velocity).abs() / delta_time;
if accel > limit {
violations.push(format!(
"'{}': acceleration {:.6} exceeds max_acceleration {:.6}",
state.name, accel, limit
));
}
}
for prev_state in prev {
if !current_names.contains(prev_state.name.as_str()) {
violations.push(format!(
"'{}': joint was present in previous command but is absent from current command",
prev_state.name
));
}
}
if violations.is_empty() {
CheckResult {
name: "acceleration_limits".to_string(),
category: "physics".to_string(),
passed: true,
details: "all joints within acceleration limits".to_string(),
}
} else {
CheckResult {
name: "acceleration_limits".to_string(),
category: "physics".to_string(),
passed: false,
details: violations.join("; "),
}
}
}