use crate::models::command::EnvironmentState;
use crate::models::profile::EnvironmentConfig;
use crate::models::verdict::CheckResult;
pub fn check_terrain_incline(env: &EnvironmentState, config: &EnvironmentConfig) -> CheckResult {
let mut violations: Vec<String> = Vec::new();
if let Some(pitch) = env.imu_pitch_rad {
if !pitch.is_finite() {
violations.push("imu_pitch_rad is NaN or infinite".to_string());
} else if pitch.abs() > config.max_safe_pitch_rad {
violations.push(format!(
"pitch {:.4} rad exceeds max_safe_pitch_rad {:.4} rad",
pitch.abs(),
config.max_safe_pitch_rad
));
}
}
if let Some(roll) = env.imu_roll_rad {
if !roll.is_finite() {
violations.push("imu_roll_rad is NaN or infinite".to_string());
} else if roll.abs() > config.max_safe_roll_rad {
violations.push(format!(
"roll {:.4} rad exceeds max_safe_roll_rad {:.4} rad",
roll.abs(),
config.max_safe_roll_rad
));
}
}
if violations.is_empty() {
CheckResult {
name: "terrain_incline".to_string(),
category: "physics".to_string(),
passed: true,
details: "terrain angles within safe limits".to_string(),
}
} else {
CheckResult {
name: "terrain_incline".to_string(),
category: "physics".to_string(),
passed: false,
details: violations.join("; "),
}
}
}
pub fn check_actuator_temperature(
env: &EnvironmentState,
config: &EnvironmentConfig,
) -> CheckResult {
if env.actuator_temperatures.is_empty() {
return CheckResult {
name: "actuator_temperature".to_string(),
category: "physics".to_string(),
passed: true,
details: "no actuator temperature data to check".to_string(),
};
}
let mut violations: Vec<String> = Vec::new();
for reading in &env.actuator_temperatures {
if !reading.temperature_celsius.is_finite() {
violations.push(format!(
"'{}': temperature is NaN or infinite",
reading.joint_name
));
} else if reading.temperature_celsius > config.max_operating_temperature_c {
violations.push(format!(
"'{}': temperature {:.1}°C exceeds max {:.1}°C",
reading.joint_name, reading.temperature_celsius, config.max_operating_temperature_c
));
}
}
if violations.is_empty() {
CheckResult {
name: "actuator_temperature".to_string(),
category: "physics".to_string(),
passed: true,
details: "all actuator temperatures within operating limits".to_string(),
}
} else {
CheckResult {
name: "actuator_temperature".to_string(),
category: "physics".to_string(),
passed: false,
details: violations.join("; "),
}
}
}
pub fn check_battery_state(env: &EnvironmentState, config: &EnvironmentConfig) -> CheckResult {
let Some(pct) = env.battery_percentage else {
return CheckResult {
name: "battery_state".to_string(),
category: "physics".to_string(),
passed: true,
details: "no battery data to check".to_string(),
};
};
if !pct.is_finite() {
return CheckResult {
name: "battery_state".to_string(),
category: "physics".to_string(),
passed: false,
details: "battery_percentage is NaN or infinite".to_string(),
};
}
if pct < config.critical_battery_pct {
CheckResult {
name: "battery_state".to_string(),
category: "physics".to_string(),
passed: false,
details: format!(
"battery {:.1}% is below critical threshold {:.1}%",
pct, config.critical_battery_pct
),
}
} else if pct < config.low_battery_pct {
CheckResult {
name: "battery_state".to_string(),
category: "physics".to_string(),
passed: true,
details: format!(
"battery {:.1}% is below low threshold {:.1}% (advisory)",
pct, config.low_battery_pct
),
}
} else {
CheckResult {
name: "battery_state".to_string(),
category: "physics".to_string(),
passed: true,
details: format!("battery {:.1}% is within operating range", pct),
}
}
}
pub fn check_communication_latency(
env: &EnvironmentState,
config: &EnvironmentConfig,
) -> CheckResult {
let Some(latency) = env.communication_latency_ms else {
return CheckResult {
name: "communication_latency".to_string(),
category: "physics".to_string(),
passed: true,
details: "no latency data to check".to_string(),
};
};
if !latency.is_finite() {
return CheckResult {
name: "communication_latency".to_string(),
category: "physics".to_string(),
passed: false,
details: "communication_latency_ms is NaN or infinite".to_string(),
};
}
if latency > config.max_latency_ms {
CheckResult {
name: "communication_latency".to_string(),
category: "physics".to_string(),
passed: false,
details: format!(
"latency {:.1} ms exceeds max_latency_ms {:.1} ms",
latency, config.max_latency_ms
),
}
} else if latency > config.warning_latency_ms {
CheckResult {
name: "communication_latency".to_string(),
category: "physics".to_string(),
passed: true,
details: format!(
"latency {:.1} ms exceeds warning threshold {:.1} ms (advisory)",
latency, config.warning_latency_ms
),
}
} else {
CheckResult {
name: "communication_latency".to_string(),
category: "physics".to_string(),
passed: true,
details: format!("latency {:.1} ms is within acceptable bounds", latency),
}
}
}
pub fn check_emergency_stop(env: &EnvironmentState) -> CheckResult {
let Some(engaged) = env.e_stop_engaged else {
return CheckResult {
name: "emergency_stop".to_string(),
category: "physics".to_string(),
passed: true,
details: "no e-stop data to check".to_string(),
};
};
if engaged {
CheckResult {
name: "emergency_stop".to_string(),
category: "physics".to_string(),
passed: false,
details: "hardware emergency stop is engaged — all commands rejected".to_string(),
}
} else {
CheckResult {
name: "emergency_stop".to_string(),
category: "physics".to_string(),
passed: true,
details: "emergency stop is not engaged".to_string(),
}
}
}