use std::{sync::mpsc, time::Duration}; use asynchronix::{ simulation::{Address, Simulation}, time::{Clock, MonotonicTime, SystemClock}, }; use satrs_minisim::{ acs::{MgmRequest, MgtRequest}, eps::PcduRequest, RequestError, SimCtrlReply, SimCtrlRequest, SimReply, SimRequest, SimTarget, }; use crate::{ acs::{MagnetometerModel, MagnetorquerModel}, eps::PcduModel, }; // The simulation controller processes requests and drives the simulation. pub struct SimController { pub sys_clock: SystemClock, pub request_receiver: mpsc::Receiver, pub reply_sender: mpsc::Sender, pub simulation: Simulation, pub mgm_addr: Address, pub pcdu_addr: Address, pub mgt_addr: Address, } impl SimController { pub fn new( sys_clock: SystemClock, request_receiver: mpsc::Receiver, reply_sender: mpsc::Sender, simulation: Simulation, mgm_addr: Address, pcdu_addr: Address, mgt_addr: Address, ) -> Self { Self { sys_clock, request_receiver, reply_sender, simulation, mgm_addr, pcdu_addr, mgt_addr, } } pub fn run(&mut self, start_time: MonotonicTime, udp_polling_interval_ms: u64) { let mut t = start_time + Duration::from_millis(udp_polling_interval_ms); self.sys_clock.synchronize(t); loop { // Check for UDP requests every millisecond. Shift the simulator ahead here to prevent // replies lying in the past. t += Duration::from_millis(udp_polling_interval_ms); self.simulation .step_until(t) .expect("simulation step failed"); self.handle_sim_requests(); self.sys_clock.synchronize(t); } } pub fn handle_sim_requests(&mut self) { loop { match self.request_receiver.try_recv() { Ok(request) => { if let Err(e) = match request.target() { SimTarget::SimCtrl => self.handle_ctrl_request(&request), SimTarget::Mgm => self.handle_mgm_request(&request), SimTarget::Mgt => self.handle_mgt_request(&request), SimTarget::Pcdu => self.handle_pcdu_request(&request), } { self.handle_invalid_request_with_valid_target(e, &request) } } Err(e) => match e { mpsc::TryRecvError::Empty => break, mpsc::TryRecvError::Disconnected => { panic!("all request sender disconnected") } }, } } } fn handle_ctrl_request(&mut self, request: &SimRequest) -> serde_json::Result<()> { let sim_ctrl_request: SimCtrlRequest = serde_json::from_str(request.request())?; match sim_ctrl_request { SimCtrlRequest::Ping => { self.reply_sender .send(SimReply::new(SimTarget::SimCtrl, SimCtrlReply::Pong)) .expect("sending reply from sim controller failed"); } } Ok(()) } fn handle_mgm_request(&mut self, request: &SimRequest) -> serde_json::Result<()> { let mgm_request: MgmRequest = serde_json::from_str(request.request())?; match mgm_request { MgmRequest::RequestSensorData => { self.simulation.send_event( MagnetometerModel::send_sensor_values, (), &self.mgm_addr, ); } } Ok(()) } fn handle_pcdu_request(&mut self, request: &SimRequest) -> serde_json::Result<()> { let pcdu_request: PcduRequest = serde_json::from_str(request.request())?; match pcdu_request { PcduRequest::RequestSwitchInfo => { self.simulation .send_event(PcduModel::request_switch_info, (), &self.pcdu_addr); } PcduRequest::SwitchDevice { switch, state } => { self.simulation.send_event( PcduModel::switch_device, (switch, state), &self.pcdu_addr, ); } } Ok(()) } fn handle_mgt_request(&mut self, request: &SimRequest) -> serde_json::Result<()> { let mgt_request: MgtRequest = serde_json::from_str(request.request())?; match mgt_request { MgtRequest::ApplyTorque { duration, dipole } => self.simulation.send_event( MagnetorquerModel::apply_torque, (duration, dipole), &self.mgt_addr, ), MgtRequest::RequestHk => self.simulation.send_event( MagnetorquerModel::request_housekeeping_data, (), &self.mgt_addr, ), } Ok(()) } fn handle_invalid_request_with_valid_target( &self, error: serde_json::Error, request: &SimRequest, ) { log::warn!( "received invalid {:?} request: {:?}", request.target(), error ); self.reply_sender .send(SimReply::new( SimTarget::SimCtrl, SimCtrlReply::from(RequestError::TargetRequestMissmatch(request.clone())), )) .expect("sending reply from sim controller failed"); } } #[cfg(test)] mod tests { use crate::test_helpers::SimTestbench; use super::*; #[test] fn test_basic_ping() { let mut sim_testbench = SimTestbench::new(); let sim_ctrl_request = SimCtrlRequest::Ping; let request = SimRequest::new(SimTarget::SimCtrl, sim_ctrl_request); sim_testbench .send_request(request) .expect("sending sim ctrl 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::SimCtrl); let reply: SimCtrlReply = serde_json::from_str(sim_reply.reply()) .expect("failed to deserialize MGM sensor values"); assert_eq!(reply, SimCtrlReply::Pong); } #[test] fn test_invalid_request() { // TODO: Implement this test. Check for the expected reply. } }