diff --git a/satrs-minisim/src/main.rs b/satrs-minisim/src/main.rs index de9036e..2469ba5 100644 --- a/satrs-minisim/src/main.rs +++ b/satrs-minisim/src/main.rs @@ -2,9 +2,10 @@ use asynchronix::model::{Model, Output}; use asynchronix::simulation::{EventSlot, Mailbox, SimInit}; use asynchronix::time::{MonotonicTime, Scheduler}; use log::warn; -use satrs::power::SwitchState; +use satrs::power::{SwitchState, SwitchStateBinary}; use serde::{Deserialize, Serialize}; use std::f64::consts::PI; +use std::future::Future; use std::net::UdpSocket; use std::time::Duration; use std::{io, thread}; @@ -36,21 +37,66 @@ const MGT_GEN_MAGNETIC_FIELD: MgmTuple = MgmTuple { pub struct MagnetometerModel { pub switch_state: SwitchState, + pub periodicity: Duration, pub external_mag_field: Option, pub sensor_values: Output, } -impl Default for MagnetometerModel { - fn default() -> Self { +impl MagnetometerModel { + fn new(periodicity: Duration) -> Self { Self { switch_state: SwitchState::Off, + periodicity, external_mag_field: None, sensor_values: Default::default(), } } -} -impl MagnetometerModel { + pub async fn start(&mut self, _: (), scheduler: &Scheduler) { + self.generate_output_self_scheduling((), scheduler).await; + } + + pub async fn switch_device(&mut self, switch_state: SwitchState, scheduler: &Scheduler) { + self.switch_state = switch_state; + self.generate_output((), scheduler).await; + } + + // Devices like magnetorquers generate a strong magnetic field which overrides the default + // model for the measured magnetic field. + pub async fn apply_external_magnetic_field( + &mut self, + field: MgmTuple, + scheduler: &Scheduler, + ) { + self.external_mag_field = Some(field); + self.generate_output((), scheduler).await; + } + + // Simple unit input to request MGM tuple for current time. + // + // Need the partially desugared function signature, see [asynchronix::time::Scheduler] docs. + #[allow(clippy::manual_async_fn)] + pub fn generate_output_self_scheduling<'a>( + &'a mut self, + _: (), + scheduler: &'a Scheduler, + ) -> impl Future + Send + 'a { + async move { + if scheduler + .schedule_event(self.periodicity, Self::generate_output_self_scheduling, ()) + .is_err() + { + warn!("output generation can only be set for a future time."); + } + self.generate_output((), scheduler).await; + } + } + + pub async fn generate_output(&mut self, _: (), scheduler: &Scheduler) { + let value = self.calculate_current_mgm_tuple(current_millis(scheduler.time())); + self.sensor_values.send(value).await; + } + fn calculate_current_mgm_tuple(&mut self, time_ms: u64) -> MgmTuple { if let SwitchState::On = self.switch_state { if let Some(ext_field) = self.external_mag_field { @@ -69,22 +115,6 @@ impl MagnetometerModel { z: 0.0, } } - - pub async fn switch_device(&mut self, switch_state: SwitchState) { - self.switch_state = switch_state; - } - - // Simple unit input to request MGM tuple for current time. - pub async fn generate_output(&mut self, _: (), scheduler: &Scheduler) { - let value = self.calculate_current_mgm_tuple(current_millis(scheduler.time())); - self.sensor_values.send(value).await; - } - - // Devices like magnetorquers generate a strong magnetic field which overrides the default - // model for the measure magnetic field. - pub async fn apply_external_magnetic_field(&mut self, field: MgmTuple) { - self.external_mag_field = Some(field); - } } impl Model for MagnetometerModel {} @@ -93,11 +123,12 @@ impl Model for MagnetometerModel {} pub struct PcduTuple {} pub enum PcduSwitches { - Mgm, - Mgt, + Mgm = 0, + Mgt = 1, } pub struct PcduModel { + pub switcher_list: Output>, pub mgm_switch: Output, pub mgt_switch: Output, } @@ -128,20 +159,38 @@ pub struct Dipole { pub struct MagnetorquerModel { switch_state: SwitchState, torquing: bool, - torque_duration: Duration, + //torque_duration: Duration, torque_dipole: Option, gen_magnetic_field: Output, } impl MagnetorquerModel { - pub async fn apply_torque(&mut self, dipole: Dipole, torque_duration: Duration) { + pub async fn apply_torque( + &mut self, + dipole: Dipole, + torque_duration: Duration, + scheduler: &Scheduler, + ) { self.torque_dipole = Some(dipole); - self.torque_duration = torque_duration; self.torquing = true; + if scheduler + .schedule_event(torque_duration, Self::clear_torque, ()) + .is_err() + { + warn!("torque clearing can only be set for a future time."); + } + self.generate_magnetic_field(()).await; + } + + pub async fn clear_torque(&mut self, _: ()) { + self.torque_dipole = None; + self.torquing = false; + self.generate_magnetic_field(()).await; } pub async fn switch_device(&mut self, switch_state: SwitchState) { self.switch_state = switch_state; + self.generate_magnetic_field(()).await; } fn calc_magnetic_field(&self, _: Dipole) -> MgmTuple { @@ -150,7 +199,9 @@ impl MagnetorquerModel { MGT_GEN_MAGNETIC_FIELD } - pub async fn generate_output(&mut self, _: ()) { + /// A torquing magnetorquer generates a magnetic field. This function can be used to apply + /// the magnetic field. + async fn generate_magnetic_field(&mut self, _: ()) { if self.switch_state != SwitchState::On || !self.torquing { return; } @@ -189,9 +240,10 @@ impl UdpServer { pub fn run(&mut self) { loop { - let mut buffer = [0u8; 1024]; // Buffer to store incoming data. - - // Block until data is received. `recv_from` returns the number of bytes read and the sender's address. + // Buffer to store incoming data. + let mut buffer = [0u8; 4096]; + // Block until data is received. `recv_from` returns the number of bytes read and the + // sender's address. let (bytes_read, src) = self .socket .recv_from(&mut buffer) @@ -231,35 +283,41 @@ pub fn current_millis(time: MonotonicTime) -> u64 { fn main() { // Instantiate models and their mailboxes. - let mut mgm_sim = MagnetometerModel::default(); + let mut mgm_sim = MagnetometerModel::new(Duration::from_millis(50)); let mgm_mailbox = Mailbox::new(); let mgm_input_addr = mgm_mailbox.address(); // Keep handles to the main input and output. let output_slot = mgm_sim.sensor_values.connect_slot().0; - let mut output_slot_2 = mgm_sim.sensor_values.connect_slot().0; + // let output_slot_2 = mgm_sim.sensor_values.connect_slot().0; // Instantiate the simulator let t0 = MonotonicTime::EPOCH; // arbitrary start time let mut simu = SimInit::new().add_model(mgm_sim, mgm_mailbox).init(t0); // This thread schedules the simulator. - thread::spawn(move || { - simu.send_event(MagnetometerModel::generate_output, (), &mgm_input_addr); - let mut tuple = output_slot_2.take().expect("expected output"); - println!("output at {:?}: {tuple:?}", simu.time()); + let sim_thread = thread::spawn(move || { + // The magnetometer will schedule itself at fixed intervals. + simu.send_event(MagnetometerModel::start, (), &mgm_input_addr); + /* for _ in 0..100 { - simu.step_by(Duration::from_millis(100)); - simu.send_event(MagnetometerModel::generate_output, (), &mgm_input_addr); - tuple = output_slot_2.take().expect("expected output"); + simu.step(); + let tuple = output_slot_2.take().expect("expected output"); println!("output at {:?}: {tuple:?}", simu.time()); } + */ + loop { + simu.step(); + } }); // This thread manages the simulator UDP server. - thread::spawn(move || { + let udp_thread = thread::spawn(move || { let mut server = UdpServer::new(output_slot).unwrap(); server.run(); }); + + sim_thread.join().expect("joining simulation thread failed"); + udp_thread.join().expect("joining UDP thread failed"); } diff --git a/satrs/src/power.rs b/satrs/src/power.rs index 1675c01..7786651 100644 --- a/satrs/src/power.rs +++ b/satrs/src/power.rs @@ -24,6 +24,33 @@ pub enum SwitchState { Faulty = 3, } +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum SwitchStateBinary { + Off = 0, + On = 1, +} + +impl TryFrom for SwitchStateBinary { + type Error = (); + fn try_from(value: SwitchState) -> Result { + match value { + SwitchState::Off => Ok(SwitchStateBinary::Off), + SwitchState::On => Ok(SwitchStateBinary::On), + _ => Err(()), + } + } +} + +impl From for SwitchState { + fn from(value: SwitchStateBinary) -> Self { + match value { + SwitchStateBinary::Off => SwitchState::Off, + SwitchStateBinary::On => SwitchState::On, + } + } +} + pub type SwitchId = u16; /// Generic trait for a device capable of turning on and off switches.