continue minisim
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good

This commit is contained in:
Robin Müller 2024-03-05 15:48:57 +01:00
parent 46f3374072
commit 9569cb76c3
2 changed files with 125 additions and 40 deletions

View File

@ -2,9 +2,10 @@ use asynchronix::model::{Model, Output};
use asynchronix::simulation::{EventSlot, Mailbox, SimInit}; use asynchronix::simulation::{EventSlot, Mailbox, SimInit};
use asynchronix::time::{MonotonicTime, Scheduler}; use asynchronix::time::{MonotonicTime, Scheduler};
use log::warn; use log::warn;
use satrs::power::SwitchState; use satrs::power::{SwitchState, SwitchStateBinary};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::f64::consts::PI; use std::f64::consts::PI;
use std::future::Future;
use std::net::UdpSocket; use std::net::UdpSocket;
use std::time::Duration; use std::time::Duration;
use std::{io, thread}; use std::{io, thread};
@ -36,21 +37,66 @@ const MGT_GEN_MAGNETIC_FIELD: MgmTuple = MgmTuple {
pub struct MagnetometerModel { pub struct MagnetometerModel {
pub switch_state: SwitchState, pub switch_state: SwitchState,
pub periodicity: Duration,
pub external_mag_field: Option<MgmTuple>, pub external_mag_field: Option<MgmTuple>,
pub sensor_values: Output<MgmTuple>, pub sensor_values: Output<MgmTuple>,
} }
impl Default for MagnetometerModel { impl MagnetometerModel {
fn default() -> Self { fn new(periodicity: Duration) -> Self {
Self { Self {
switch_state: SwitchState::Off, switch_state: SwitchState::Off,
periodicity,
external_mag_field: None, external_mag_field: None,
sensor_values: Default::default(), sensor_values: Default::default(),
} }
} }
}
impl MagnetometerModel { pub async fn start(&mut self, _: (), scheduler: &Scheduler<Self>) {
self.generate_output_self_scheduling((), scheduler).await;
}
pub async fn switch_device(&mut self, switch_state: SwitchState, scheduler: &Scheduler<Self>) {
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>,
) {
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<Self>,
) -> impl Future<Output = ()> + 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<Self>) {
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 { fn calculate_current_mgm_tuple(&mut self, time_ms: u64) -> MgmTuple {
if let SwitchState::On = self.switch_state { if let SwitchState::On = self.switch_state {
if let Some(ext_field) = self.external_mag_field { if let Some(ext_field) = self.external_mag_field {
@ -69,22 +115,6 @@ impl MagnetometerModel {
z: 0.0, 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<Self>) {
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 {} impl Model for MagnetometerModel {}
@ -93,11 +123,12 @@ impl Model for MagnetometerModel {}
pub struct PcduTuple {} pub struct PcduTuple {}
pub enum PcduSwitches { pub enum PcduSwitches {
Mgm, Mgm = 0,
Mgt, Mgt = 1,
} }
pub struct PcduModel { pub struct PcduModel {
pub switcher_list: Output<Vec<SwitchStateBinary>>,
pub mgm_switch: Output<SwitchState>, pub mgm_switch: Output<SwitchState>,
pub mgt_switch: Output<SwitchState>, pub mgt_switch: Output<SwitchState>,
} }
@ -128,20 +159,38 @@ pub struct Dipole {
pub struct MagnetorquerModel { pub struct MagnetorquerModel {
switch_state: SwitchState, switch_state: SwitchState,
torquing: bool, torquing: bool,
torque_duration: Duration, //torque_duration: Duration,
torque_dipole: Option<Dipole>, torque_dipole: Option<Dipole>,
gen_magnetic_field: Output<MgmTuple>, gen_magnetic_field: Output<MgmTuple>,
} }
impl MagnetorquerModel { 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>,
) {
self.torque_dipole = Some(dipole); self.torque_dipole = Some(dipole);
self.torque_duration = torque_duration;
self.torquing = true; 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) { pub async fn switch_device(&mut self, switch_state: SwitchState) {
self.switch_state = switch_state; self.switch_state = switch_state;
self.generate_magnetic_field(()).await;
} }
fn calc_magnetic_field(&self, _: Dipole) -> MgmTuple { fn calc_magnetic_field(&self, _: Dipole) -> MgmTuple {
@ -150,7 +199,9 @@ impl MagnetorquerModel {
MGT_GEN_MAGNETIC_FIELD 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 { if self.switch_state != SwitchState::On || !self.torquing {
return; return;
} }
@ -189,9 +240,10 @@ impl UdpServer {
pub fn run(&mut self) { pub fn run(&mut self) {
loop { loop {
let mut buffer = [0u8; 1024]; // Buffer to store incoming data. // 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. // Block until data is received. `recv_from` returns the number of bytes read and the
// sender's address.
let (bytes_read, src) = self let (bytes_read, src) = self
.socket .socket
.recv_from(&mut buffer) .recv_from(&mut buffer)
@ -231,35 +283,41 @@ pub fn current_millis(time: MonotonicTime) -> u64 {
fn main() { fn main() {
// Instantiate models and their mailboxes. // 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_mailbox = Mailbox::new();
let mgm_input_addr = mgm_mailbox.address(); let mgm_input_addr = mgm_mailbox.address();
// Keep handles to the main input and output. // Keep handles to the main input and output.
let output_slot = mgm_sim.sensor_values.connect_slot().0; 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 // Instantiate the simulator
let t0 = MonotonicTime::EPOCH; // arbitrary start time let t0 = MonotonicTime::EPOCH; // arbitrary start time
let mut simu = SimInit::new().add_model(mgm_sim, mgm_mailbox).init(t0); let mut simu = SimInit::new().add_model(mgm_sim, mgm_mailbox).init(t0);
// This thread schedules the simulator. // This thread schedules the simulator.
thread::spawn(move || { let sim_thread = thread::spawn(move || {
simu.send_event(MagnetometerModel::generate_output, (), &mgm_input_addr); // The magnetometer will schedule itself at fixed intervals.
let mut tuple = output_slot_2.take().expect("expected output"); simu.send_event(MagnetometerModel::start, (), &mgm_input_addr);
println!("output at {:?}: {tuple:?}", simu.time()); /*
for _ in 0..100 { for _ in 0..100 {
simu.step_by(Duration::from_millis(100)); simu.step();
simu.send_event(MagnetometerModel::generate_output, (), &mgm_input_addr); let tuple = output_slot_2.take().expect("expected output");
tuple = output_slot_2.take().expect("expected output");
println!("output at {:?}: {tuple:?}", simu.time()); println!("output at {:?}: {tuple:?}", simu.time());
} }
*/
loop {
simu.step();
}
}); });
// This thread manages the simulator UDP server. // This thread manages the simulator UDP server.
thread::spawn(move || { let udp_thread = thread::spawn(move || {
let mut server = UdpServer::new(output_slot).unwrap(); let mut server = UdpServer::new(output_slot).unwrap();
server.run(); server.run();
}); });
sim_thread.join().expect("joining simulation thread failed");
udp_thread.join().expect("joining UDP thread failed");
} }

View File

@ -24,6 +24,33 @@ pub enum SwitchState {
Faulty = 3, Faulty = 3,
} }
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SwitchStateBinary {
Off = 0,
On = 1,
}
impl TryFrom<SwitchState> for SwitchStateBinary {
type Error = ();
fn try_from(value: SwitchState) -> Result<Self, Self::Error> {
match value {
SwitchState::Off => Ok(SwitchStateBinary::Off),
SwitchState::On => Ok(SwitchStateBinary::On),
_ => Err(()),
}
}
}
impl From<SwitchStateBinary> for SwitchState {
fn from(value: SwitchStateBinary) -> Self {
match value {
SwitchStateBinary::Off => SwitchState::Off,
SwitchStateBinary::On => SwitchState::On,
}
}
}
pub type SwitchId = u16; pub type SwitchId = u16;
/// Generic trait for a device capable of turning on and off switches. /// Generic trait for a device capable of turning on and off switches.