First version of asynchronix based mini simulator
Some checks are pending
Rust/sat-rs/pipeline/pr-main Build queued...
Some checks are pending
Rust/sat-rs/pipeline/pr-main Build queued...
- Basic simulator with 3 devices - Can be driven via a UDP interface - Design allows to drive the simulation via different interface in the future by using Request/Reply messaging.
This commit is contained in:
187
satrs-minisim/src/eps.rs
Normal file
187
satrs-minisim/src/eps.rs
Normal file
@ -0,0 +1,187 @@
|
||||
use std::{collections::HashMap, sync::mpsc, time::Duration};
|
||||
|
||||
use asynchronix::{
|
||||
model::{Model, Output},
|
||||
time::Scheduler,
|
||||
};
|
||||
use satrs::power::SwitchStateBinary;
|
||||
use satrs_minisim::{
|
||||
eps::{PcduReply, PcduSwitch, SwitchMap},
|
||||
SimReply, SimTarget,
|
||||
};
|
||||
|
||||
pub const SWITCH_INFO_DELAY_MS: u64 = 10;
|
||||
|
||||
pub struct PcduModel {
|
||||
pub switcher_map: SwitchMap,
|
||||
pub mgm_switch: Output<SwitchStateBinary>,
|
||||
pub mgt_switch: Output<SwitchStateBinary>,
|
||||
pub reply_sender: mpsc::Sender<SimReply>,
|
||||
}
|
||||
|
||||
impl PcduModel {
|
||||
pub fn new(reply_sender: mpsc::Sender<SimReply>) -> Self {
|
||||
let mut switcher_map = HashMap::new();
|
||||
switcher_map.insert(PcduSwitch::Mgm, SwitchStateBinary::Off);
|
||||
switcher_map.insert(PcduSwitch::Mgt, SwitchStateBinary::Off);
|
||||
|
||||
Self {
|
||||
switcher_map,
|
||||
mgm_switch: Output::new(),
|
||||
mgt_switch: Output::new(),
|
||||
reply_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn request_switch_info(&mut self, _: (), scheduler: &Scheduler<Self>) {
|
||||
scheduler
|
||||
.schedule_event(
|
||||
Duration::from_millis(SWITCH_INFO_DELAY_MS),
|
||||
Self::send_switch_info,
|
||||
(),
|
||||
)
|
||||
.expect("requesting switch info failed");
|
||||
}
|
||||
|
||||
pub fn send_switch_info(&mut self) {
|
||||
let switch_info = PcduReply::SwitchInfo(self.switcher_map.clone());
|
||||
let reply = SimReply::new(SimTarget::Pcdu, switch_info);
|
||||
self.reply_sender.send(reply).unwrap();
|
||||
}
|
||||
|
||||
pub async fn switch_device(
|
||||
&mut self,
|
||||
switch_and_target_state: (PcduSwitch, SwitchStateBinary),
|
||||
) {
|
||||
let val = self
|
||||
.switcher_map
|
||||
.get_mut(&switch_and_target_state.0)
|
||||
.unwrap_or_else(|| panic!("switch {:?} not found", switch_and_target_state.0));
|
||||
*val = switch_and_target_state.1;
|
||||
match switch_and_target_state.0 {
|
||||
PcduSwitch::Mgm => {
|
||||
self.mgm_switch.send(switch_and_target_state.1).await;
|
||||
}
|
||||
PcduSwitch::Mgt => {
|
||||
self.mgt_switch.send(switch_and_target_state.1).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Model for PcduModel {}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
use satrs_minisim::{eps::PcduRequest, SimRequest, SimTarget};
|
||||
|
||||
use crate::test_helpers::SimTestbench;
|
||||
|
||||
fn switch_device(
|
||||
sim_testbench: &mut SimTestbench,
|
||||
switch: PcduSwitch,
|
||||
target: SwitchStateBinary,
|
||||
) {
|
||||
let pcdu_request = PcduRequest::SwitchDevice {
|
||||
switch,
|
||||
state: target,
|
||||
};
|
||||
let request = SimRequest::new(SimTarget::Pcdu, pcdu_request);
|
||||
sim_testbench
|
||||
.send_request(request)
|
||||
.expect("sending MGM switch request failed");
|
||||
sim_testbench.handle_sim_requests();
|
||||
sim_testbench.step();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn switch_device_off(sim_testbench: &mut SimTestbench, switch: PcduSwitch) {
|
||||
switch_device(sim_testbench, switch, SwitchStateBinary::Off);
|
||||
}
|
||||
pub(crate) fn switch_device_on(sim_testbench: &mut SimTestbench, switch: PcduSwitch) {
|
||||
switch_device(sim_testbench, switch, SwitchStateBinary::On);
|
||||
}
|
||||
|
||||
pub(crate) fn get_all_off_switch_map() -> SwitchMap {
|
||||
let mut switcher_map = SwitchMap::new();
|
||||
switcher_map.insert(super::PcduSwitch::Mgm, super::SwitchStateBinary::Off);
|
||||
switcher_map.insert(super::PcduSwitch::Mgt, super::SwitchStateBinary::Off);
|
||||
switcher_map
|
||||
}
|
||||
|
||||
fn check_switch_state(sim_testbench: &mut SimTestbench, expected_switch_map: &SwitchMap) {
|
||||
let pcdu_request = PcduRequest::RequestSwitchInfo;
|
||||
let request = SimRequest::new(SimTarget::Pcdu, pcdu_request);
|
||||
sim_testbench
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests();
|
||||
sim_testbench.step();
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
assert_eq!(sim_reply.target(), SimTarget::Pcdu);
|
||||
let pcdu_reply: PcduReply = serde_json::from_str(&sim_reply.reply())
|
||||
.expect("failed to deserialize PCDU switch info");
|
||||
match pcdu_reply {
|
||||
PcduReply::SwitchInfo(switch_map) => {
|
||||
assert_eq!(switch_map, *expected_switch_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_pcdu_switching_single_switch(switch: PcduSwitch, target: SwitchStateBinary) {
|
||||
let mut sim_testbench = SimTestbench::new();
|
||||
switch_device(&mut sim_testbench, switch, target);
|
||||
let mut switcher_map = get_all_off_switch_map();
|
||||
*switcher_map.get_mut(&switch).unwrap() = target;
|
||||
check_switch_state(&mut sim_testbench, &switcher_map);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pcdu_switcher_request() {
|
||||
let mut sim_testbench = SimTestbench::new();
|
||||
let pcdu_request = PcduRequest::RequestSwitchInfo;
|
||||
let request = SimRequest::new(SimTarget::Pcdu, pcdu_request);
|
||||
sim_testbench
|
||||
.send_request(request)
|
||||
.expect("sending MGM request failed");
|
||||
sim_testbench.handle_sim_requests();
|
||||
sim_testbench.step_by(Duration::from_millis(1));
|
||||
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_none());
|
||||
// Reply takes 20ms
|
||||
sim_testbench.step_by(Duration::from_millis(25));
|
||||
let sim_reply = sim_testbench.try_receive_next_reply();
|
||||
assert!(sim_reply.is_some());
|
||||
let sim_reply = sim_reply.unwrap();
|
||||
assert_eq!(sim_reply.target(), SimTarget::Pcdu);
|
||||
let pcdu_reply: PcduReply = serde_json::from_str(&sim_reply.reply())
|
||||
.expect("failed to deserialize PCDU switch info");
|
||||
match pcdu_reply {
|
||||
PcduReply::SwitchInfo(switch_map) => {
|
||||
assert_eq!(switch_map, get_all_off_switch_map());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pcdu_switching_mgm_on() {
|
||||
test_pcdu_switching_single_switch(PcduSwitch::Mgm, SwitchStateBinary::On);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pcdu_switching_mgt_on() {
|
||||
test_pcdu_switching_single_switch(PcduSwitch::Mgt, SwitchStateBinary::On);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pcdu_switching_mgt_off() {
|
||||
test_pcdu_switching_single_switch(PcduSwitch::Mgt, SwitchStateBinary::On);
|
||||
test_pcdu_switching_single_switch(PcduSwitch::Mgt, SwitchStateBinary::Off);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user