This commit is contained in:
parent
46f3374072
commit
9569cb76c3
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user