use arika::frame::{Body, Vec3};
use super::command::Command;
use super::error::PluginError;
#[derive(Debug, Clone, Default)]
pub struct ActuatorBundle {
commanded_magnetic_moment: Option<Vec3<Body>>,
commanded_rw_torque: Option<Vec3<Body>>,
}
impl ActuatorBundle {
pub fn new() -> Self {
Self::default()
}
pub fn apply(&mut self, cmd: &Command) -> Result<(), PluginError> {
if !cmd.is_finite() {
return Err(PluginError::BadCommand(format!("{cmd:?}")));
}
if let Some(m) = cmd.magnetic_moment {
self.commanded_magnetic_moment = Some(m);
}
if let Some(t) = cmd.rw_torque {
self.commanded_rw_torque = Some(t);
}
Ok(())
}
pub fn magnetic_moment(&self) -> Vec3<Body> {
self.commanded_magnetic_moment.unwrap_or_else(Vec3::zeros)
}
pub fn has_magnetic_moment_command(&self) -> bool {
self.commanded_magnetic_moment.is_some()
}
pub fn rw_torque(&self) -> Vec3<Body> {
self.commanded_rw_torque.unwrap_or_else(Vec3::zeros)
}
pub fn has_rw_torque_command(&self) -> bool {
self.commanded_rw_torque.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_stores_magnetic_moment() {
let mut bundle = ActuatorBundle::new();
assert!(!bundle.has_magnetic_moment_command());
assert_eq!(bundle.magnetic_moment(), Vec3::zeros());
let m = Vec3::new(1.0, -2.0, 3.0);
bundle.apply(&Command::magnetic_moment(m)).unwrap();
assert!(bundle.has_magnetic_moment_command());
assert_eq!(bundle.magnetic_moment(), m);
}
#[test]
fn apply_stores_rw_torque() {
let mut bundle = ActuatorBundle::new();
assert!(!bundle.has_rw_torque_command());
assert_eq!(bundle.rw_torque(), Vec3::zeros());
let t = Vec3::new(0.01, -0.02, 0.03);
bundle.apply(&Command::rw_torque(t)).unwrap();
assert!(bundle.has_rw_torque_command());
assert_eq!(bundle.rw_torque(), t);
}
#[test]
fn apply_rejects_nan() {
let mut bundle = ActuatorBundle::new();
let bad = Command::magnetic_moment(Vec3::new(1.0, f64::NAN, 0.0));
let err = bundle.apply(&bad).unwrap_err();
match err {
PluginError::BadCommand(_) => {}
other => panic!("unexpected error: {other:?}"),
}
assert!(!bundle.has_magnetic_moment_command());
}
#[test]
fn apply_rw_rejects_nan() {
let mut bundle = ActuatorBundle::new();
let bad = Command::rw_torque(Vec3::new(0.0, f64::INFINITY, 0.0));
assert!(bundle.apply(&bad).is_err());
assert!(!bundle.has_rw_torque_command());
}
#[test]
fn multi_command_retains_both() {
let mut bundle = ActuatorBundle::new();
bundle
.apply(&Command::magnetic_moment(Vec3::new(1.0, 0.0, 0.0)))
.unwrap();
bundle
.apply(&Command::rw_torque(Vec3::new(0.0, 0.1, 0.0)))
.unwrap();
assert_eq!(bundle.magnetic_moment(), Vec3::new(1.0, 0.0, 0.0));
assert_eq!(bundle.rw_torque(), Vec3::new(0.0, 0.1, 0.0));
}
#[test]
fn single_command_with_both_fields() {
let mut bundle = ActuatorBundle::new();
let cmd = Command {
magnetic_moment: Some(Vec3::new(1.0, 0.0, 0.0)),
rw_torque: Some(Vec3::new(0.0, 0.1, 0.0)),
};
bundle.apply(&cmd).unwrap();
assert_eq!(bundle.magnetic_moment(), Vec3::new(1.0, 0.0, 0.0));
assert_eq!(bundle.rw_torque(), Vec3::new(0.0, 0.1, 0.0));
}
}