diff --git a/satrs-example/models/src/lib.rs b/satrs-example/models/src/lib.rs index 8e88c6d..9819b7f 100644 --- a/satrs-example/models/src/lib.rs +++ b/satrs-example/models/src/lib.rs @@ -6,6 +6,7 @@ use spacepackets::{ pub mod ccsds; pub mod control; +pub mod mgm; pub mod pcdu; #[derive( diff --git a/satrs-example/models/src/mgm.rs b/satrs-example/models/src/mgm.rs new file mode 100644 index 0000000..63f53a9 --- /dev/null +++ b/satrs-example/models/src/mgm.rs @@ -0,0 +1,23 @@ +pub mod request { + #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] + pub enum Request { + Ping, + } +} + +pub mod response { + use crate::Message; + + #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] + pub enum Response { + Ok, + } + + impl Message for Response { + fn message_type(&self) -> crate::MessageType { + match self { + Response::Ok => crate::MessageType::Verification, + } + } + } +} diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs index 0c33a2e..51ef317 100644 --- a/satrs-example/src/acs/mgm.rs +++ b/satrs-example/src/acs/mgm.rs @@ -1,9 +1,9 @@ use models::ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}; use models::pcdu::SwitchId; -use models::ComponentId; +use models::{mgm, ComponentId}; use satrs::hk::{HkRequest, HkRequestVariant}; use satrs::mode_tree::{ModeChild, ModeNode}; -use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender}; +use satrs::spacepackets::CcsdsPacketIdAndPsc; use satrs_example::{DeviceMode, TimestampHelper}; use satrs_minisim::acs::lis3mdl::{ MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR, @@ -22,6 +22,7 @@ use satrs::mode::{ use satrs::request::{GenericMessage, MessageMetadata}; use satrs_example::config::components::NO_SENDER; +use crate::ccsds::pack_ccsds_tm_packet_for_now; use crate::eps::PowerSwitchHelper; use crate::spi::SpiInterface; @@ -167,6 +168,7 @@ pub struct MgmHandlerLis3Mdl { } impl MgmHandlerLis3Mdl { + #[allow(clippy::too_many_arguments)] pub fn new( id: ComponentId, dev_str: &'static str, @@ -194,7 +196,7 @@ impl MgmHandlerLis3Mdl { pub fn periodic_operation(&mut self) { self.stamp_helper.update_from_now(); // Handle requests. - self.handle_tc(); + self.handle_telecommands(); self.handle_mode_requests(); if let Some(target_mode_submode) = self.mode_helpers.target { self.handle_mode_transition(target_mode_submode); @@ -205,32 +207,53 @@ impl MgmHandlerLis3Mdl { } } - pub fn handle_tc(&mut self) { + pub fn handle_telecommands(&mut self) { loop { - //match self.tc_rx.try_recv() { - /* - Ok(ref msg) => match &msg.message { - CompositeRequest::Hk(hk_request) => { - self.handle_hk_request(&msg.requestor_info, hk_request) + match self.tc_rx.try_recv() { + Ok(packet) => { + let tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&packet.sp_header); + match postcard::from_bytes::(&packet.payload) { + Ok(request) => { + log::info!( + "received request {:?} with TC ID {:#010x}", + request, + tc_id.raw() + ); + match request { + mgm::request::Request::Ping => { + self.send_telemetry(Some(tc_id), mgm::response::Response::Ok) + } + } + } + Err(e) => { + log::warn!("failed to deserialize request: {}", e); + } + } } - // TODO: This object does not have actions (yet).. Still send back completion failure - // reply. - CompositeRequest::Action(_action_req) => {} - }, + Err(e) => match e { + std::sync::mpsc::TryRecvError::Empty => break, + std::sync::mpsc::TryRecvError::Disconnected => { + log::warn!("packet sender disconnected") + } + }, + } + } + } - Err(e) => { - if e != mpsc::TryRecvError::Empty { - log::warn!( - "{}: failed to receive composite request: {:?}", - self.dev_str, - e - ); - } else { - break; + pub fn send_telemetry( + &self, + tc_id: Option, + response: mgm::response::Response, + ) { + match pack_ccsds_tm_packet_for_now(self.id, tc_id, &response) { + Ok(packet) => { + if let Err(e) = self.tm_tx.send(packet) { + log::warn!("failed to send TM packet: {}", e); } } - */ - //} + Err(e) => { + log::warn!("failed to pack TM packet: {}", e); + } } } @@ -477,7 +500,7 @@ mod tests { }; use models::{ - pcdu::{SwitchRequest, SwitchStateBinary}, + pcdu::{SwitchRequest, SwitchState, SwitchStateBinary}, ComponentId, }; use satrs::{ @@ -488,7 +511,7 @@ mod tests { }; use satrs_minisim::acs::lis3mdl::MgmLis3RawValues; - use crate::eps::{pcdu::SharedSwitchSet, TestSwitchHelper}; + use crate::eps::pcdu::{SharedSwitchSet, SwitchMap, SwitchSet}; use super::*; @@ -516,7 +539,7 @@ mod tests { #[allow(dead_code)] pub struct MgmTestbench { pub mode_request_tx: mpsc::SyncSender>, - pub mode_reply_rx_to_pus: mpsc::Receiver>, + pub mode_reply_rx_to_ground: mpsc::Receiver>, pub mode_reply_rx_to_parent: mpsc::Receiver>, pub shared_switch_set: SharedSwitchSet, pub tc_tx: mpsc::SyncSender, @@ -576,7 +599,10 @@ mod tests { let (_tm_tx, tm_rx) = mpsc::sync_channel(10); let (switcher_tx, switch_rx) = mpsc::sync_channel(10); let shared_mgm_set = Arc::default(); - let shared_switch_set = SharedSwitchSet::default(); + let mut switch_map = SwitchMap::new(); + switch_map.insert(SwitchId::Mgm0, SwitchState::Off); + let switch_map = SwitchSet::new(switch_map); + let shared_switch_set = SharedSwitchSet::new(Mutex::new(switch_map)); let mut handler = MgmHandlerLis3Mdl::new( ComponentId::AcsMgm0, "TEST_MGM", @@ -591,7 +617,7 @@ mod tests { handler.add_mode_parent(ComponentId::AcsMgmAssembly as u32, reply_tx_to_parent); Self { mode_request_tx: request_tx, - mode_reply_rx_to_pus: reply_rx_to_ground, + mode_reply_rx_to_ground: reply_rx_to_ground, mode_reply_rx_to_parent: reply_rx_to_parent, shared_switch_set, switch_rx, @@ -642,26 +668,22 @@ mod tests { assert_eq!(testbench.handler.mode_and_submode().submode(), 0); // Verify power switch handling. - /* - let mut switch_requests = testbench.handler.switch_helper.switch_requests.borrow_mut(); - assert_eq!(switch_requests.len(), 1); - let switch_req = switch_requests.pop_front().expect("no switch request"); - assert_eq!(switch_req.target_state, SwitchStateBinary::On); - assert_eq!(switch_req.switch_id, SwitchId::Mgm0); - let mut switch_info_requests = testbench - .handler - .switch_helper - .switch_info_requests - .borrow_mut(); - assert_eq!(switch_info_requests.len(), 1); - let switch_info_req = switch_info_requests.pop_front().expect("no switch request"); - */ let switch_req = testbench.switch_rx.try_recv().expect("no switch request"); assert_eq!(switch_req.message.switch_id, SwitchId::Mgm0); assert_eq!(switch_req.message.target_state, SwitchStateBinary::On); + // This simulates one cycle for the power switch to update. + testbench + .shared_switch_set + .lock() + .unwrap() + .set_switch_state(SwitchId::Mgm0, SwitchState::On); + + // Now the power switch is updated and the mode request should be completed. + testbench.handler.periodic_operation(); + let mode_reply = testbench - .mode_reply_rx_to_pus + .mode_reply_rx_to_ground .try_recv() .expect("no mode reply generated"); match mode_reply.message { @@ -672,7 +694,7 @@ mod tests { _ => panic!("unexpected mode reply"), } // The device should have been polled once. - assert_eq!(testbench.handler.com_interface.call_count, 1); + assert_eq!(testbench.handler.com_interface.call_count, 2); let mgm_set = *testbench.handler.shared_mgm_set.lock().unwrap(); assert!(mgm_set.x < 0.001); assert!(mgm_set.y < 0.001); diff --git a/satrs-example/src/control.rs b/satrs-example/src/control.rs index 314f556..cf726c9 100644 --- a/satrs-example/src/control.rs +++ b/satrs-example/src/control.rs @@ -20,6 +20,10 @@ impl Controller { } pub fn periodic_operation(&mut self) { + self.handle_telecommands(); + } + + pub fn handle_telecommands(&mut self) { loop { match self.tc_rx.try_recv() { Ok(packet) => { @@ -32,9 +36,8 @@ impl Controller { tc_id.raw() ); match request { - control::request::Request::Ping => { - self.send_tm(Some(tc_id), control::response::Response::Ok) - } + control::request::Request::Ping => self + .send_telemetry(Some(tc_id), control::response::Response::Ok), } } Err(e) => { @@ -52,7 +55,7 @@ impl Controller { } } - pub fn send_tm( + pub fn send_telemetry( &self, tc_id: Option, response: control::response::Response, diff --git a/satrs-example/src/eps/pcdu.rs b/satrs-example/src/eps/pcdu.rs index 34feca5..239bb41 100644 --- a/satrs-example/src/eps/pcdu.rs +++ b/satrs-example/src/eps/pcdu.rs @@ -31,12 +31,34 @@ use strum::IntoEnumIterator as _; use crate::ccsds::pack_ccsds_tm_packet_for_now; -#[derive(Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SwitchSet { pub valid: bool, pub switch_map: SwitchMap, } +impl SwitchSet { + pub fn new(switch_map: SwitchMap) -> Self { + Self { + valid: true, + switch_map, + } + } + + pub fn new_with_init_switches_unknown() -> Self { + let wrapper = SwitchMapWrapper::default(); + Self::new(wrapper.0) + } + + pub fn set_switch_state(&mut self, switch_id: SwitchId, state: SwitchState) -> bool { + if !self.switch_map.contains_key(&switch_id) { + return false; + } + *self.switch_map.get_mut(&switch_id).unwrap() = state; + true + } +} + pub type SwitchMap = HashMap; pub struct SwitchMapWrapper(pub SwitchMap); @@ -375,19 +397,16 @@ impl PcduHandler { pub fn handle_switch_requests(&mut self) { loop { match self.switch_request_rx.try_recv() { - Ok(switch_req) => match SwitchId::try_from(switch_req.message.switch_id()) { - Ok(pcdu_switch) => { - let pcdu_req = PcduRequest::SwitchDevice { - switch: pcdu_switch, - state: switch_req.message.target_state(), - }; - let pcdu_req_ser = serde_json::to_string(&pcdu_req).unwrap(); - self.com_interface - .send(pcdu_req_ser.as_bytes()) - .expect("failed to send switch request to PCDU"); - } - Err(e) => todo!("failed to convert switch ID {:?} to typed PCDU switch", e), - }, + Ok(switch_req) => { + let pcdu_req = PcduRequest::SwitchDevice { + switch: switch_req.message.switch_id(), + state: switch_req.message.target_state(), + }; + let pcdu_req_ser = serde_json::to_string(&pcdu_req).unwrap(); + self.com_interface + .send(pcdu_req_ser.as_bytes()) + .expect("failed to send switch request to PCDU"); + } Err(e) => match e { mpsc::TryRecvError::Empty => break, mpsc::TryRecvError::Disconnected => { @@ -581,7 +600,8 @@ mod tests { let (tc_tx, tc_rx) = mpsc::sync_channel(5); let (tm_tx, tm_rx) = mpsc::sync_channel(5); let (switch_request_tx, switch_reqest_rx) = mpsc::channel(); - let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default())); + let shared_switch_map = + Arc::new(Mutex::new(SwitchSet::new_with_init_switches_unknown())); let mut handler = PcduHandler::new( mode_node, tc_rx, @@ -693,7 +713,7 @@ mod tests { )) .expect("failed to send mode request"); let switch_map_shared = testbench.handler.shared_switch_map.lock().unwrap(); - assert!(!switch_map_shared.valid); + assert!(switch_map_shared.valid); drop(switch_map_shared); testbench.handler.periodic_operation(OpCode::RegularOp); testbench diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index 69da0ed..4c4afbf 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -38,6 +38,7 @@ use tmtc::{tc_source::TcSourceTask, tm_sink::TmSink}; use crate::{ acs::mgm::{MgmHandlerLis3Mdl, SpiDummyInterface, SpiSimInterface, SpiSimInterfaceWrapper}, control::Controller, + eps::pcdu::SwitchSet, interface::udp::UdpTmHandlerWithChannel, tmtc::tc_source::CcsdsDistributor, }; @@ -46,7 +47,6 @@ mod acs; mod ccsds; mod control; mod eps; -//mod hk; mod interface; mod logger; mod spi; @@ -125,7 +125,7 @@ fn main() { let mut tm_sink = TmSink::new(sync_tm_tcp_source, tm_sink_rx, tm_server_tx); - let shared_switch_set = Arc::new(Mutex::default()); + let shared_switch_set = Arc::new(Mutex::new(SwitchSet::new_with_init_switches_unknown())); let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20); let switch_helper = PowerSwitchHelper::new(switch_request_tx, shared_switch_set.clone()); diff --git a/satrs-example/src/tmtc/tm_sink.rs b/satrs-example/src/tmtc/tm_sink.rs index 2dd020e..fe712f2 100644 --- a/satrs-example/src/tmtc/tm_sink.rs +++ b/satrs-example/src/tmtc/tm_sink.rs @@ -162,8 +162,8 @@ impl TmSink { let zero_copy_writer = PusTmZeroCopyWriter::new(&mut tm_raw, MIN_CDS_FIELD_LEN, true) .expect("Creating TM zero copy writer failed"); - self.common.apply_packet_processing(zero_copy_writer); */ + //self.common.apply_packet_processing(zero_copy_writer); self.common.sync_tm_tcp_source.add_tm(&tm.to_vec()); self.tm_server_tx .send(tm)