diff --git a/satrs-example/Cargo.toml b/satrs-example/Cargo.toml index 684d432..af13dad 100644 --- a/satrs-example/Cargo.toml +++ b/satrs-example/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "satrs-example" version = "0.1.1" -edition = "2021" +edition = "2024" authors = ["Robin Mueller "] default-run = "satrs-example" homepage = "https://egit.irs.uni-stuttgart.de/rust/sat-rs" @@ -18,11 +18,11 @@ csv = "1" num_enum = "0.7" thiserror = "2" lazy_static = "1" -strum = { version = "0.27", features = ["derive"] } +strum = { version = "0.28", features = ["derive"] } derive-new = "0.7" cfg-if = "1" arbitrary-int = "2" -bitbybit = "1.4" +bitbybit = "2" postcard = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/satrs-example/client/Cargo.toml b/satrs-example/client/Cargo.toml index 56a97e7..692333d 100644 --- a/satrs-example/client/Cargo.toml +++ b/satrs-example/client/Cargo.toml @@ -12,7 +12,7 @@ serde = { version = "1" } satrs-example = { path = ".." } models = { path = "../models" } spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false } -bitbybit = "1.4" +bitbybit = "2" arbitrary-int = "2" ctrlc = { version = "3.5" } postcard = { version = "1" } diff --git a/satrs-example/minisim/Cargo.toml b/satrs-example/minisim/Cargo.toml index c54ef13..ec69ee2 100644 --- a/satrs-example/minisim/Cargo.toml +++ b/satrs-example/minisim/Cargo.toml @@ -11,7 +11,7 @@ serde_json = "1" log = "0.4" thiserror = "2" fern = "0.7" -strum = { version = "0.27", features = ["derive"] } +strum = { version = "0.28", features = ["derive"] } num_enum = "0.7" humantime = "2" tai-time = { version = "0.3", features = ["serde"] } diff --git a/satrs-example/models/Cargo.toml b/satrs-example/models/Cargo.toml index aacf377..e145bc4 100644 --- a/satrs-example/models/Cargo.toml +++ b/satrs-example/models/Cargo.toml @@ -8,8 +8,8 @@ serde = { version = "1", features = ["derive"] } spacepackets = { version = "0.17", git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false } satrs = { path = "../../satrs" } num_enum = { version = "0.7" } -strum = { version = "0.27", features = ["derive"] } +strum = { version = "0.28", features = ["derive"] } postcard = { version = "1" } thiserror = { version = "2" } -bitbybit = "1.4" +bitbybit = "2" arbitrary-int = "2" diff --git a/satrs-example/models/src/lib.rs b/satrs-example/models/src/lib.rs index 02707f1..4bdc2fc 100644 --- a/satrs-example/models/src/lib.rs +++ b/satrs-example/models/src/lib.rs @@ -163,7 +163,6 @@ pub enum DeviceMode { On = 1, /// Normal operation mode where periodic polling might be done as well. Normal = 2, - Unknown = 3, } #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs index 2d4fb3b..7288641 100644 --- a/satrs-example/src/acs/mgm.rs +++ b/satrs-example/src/acs/mgm.rs @@ -1,14 +1,13 @@ -use models::ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}; -use models::mgm::response::ModeFailure; use models::mgm::MgmData; +use models::mgm::response::ModeFailure; use models::pcdu::SwitchId; -use models::{mgm, ComponentId, DeviceMode, HkRequestType}; +use models::{ComponentId, DeviceMode, HkRequestType, mgm}; use satrs::spacepackets::CcsdsPacketIdAndPsc; -use satrs_example::{HkHelperSingleSet, ModeHelper, TimestampHelper, TransitionState}; -use satrs_minisim::acs::lis3mdl::{ - MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR, -}; +use satrs_example::{HkHelperSingleSet, ModeHelper, TimestampHelper, TmtcQueues}; use satrs_minisim::acs::MgmRequestLis3Mdl; +use satrs_minisim::acs::lis3mdl::{ + FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR, MgmLis3MdlReply, MgmLis3RawValues, +}; use satrs_minisim::{SerializableSimMsgPayload, SimReply, SimRequest}; use std::sync::mpsc; use std::sync::{Arc, Mutex}; @@ -49,12 +48,21 @@ impl MgmId { } } +#[derive(Default, Debug, PartialEq, Eq)] +pub enum TransitionState { + #[default] + Idle, + PowerSwitching, + Done, +} + pub enum ModeRequest { SetMode(DeviceMode), ReadMode, } pub enum ModeReport { + /// New mode has been set. Mode(DeviceMode), /// Setting a mode timed out. SetModeTimeout, @@ -73,6 +81,21 @@ impl SpiDummyInterface { } } +#[derive(Default)] +pub struct TestSpiInterface { + pub call_count: u32, + pub next_mgm_data: MgmLis3RawValues, +} + +impl TestSpiInterface { + fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) { + rx[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2].copy_from_slice(&self.next_mgm_data.x.to_le_bytes()); + rx[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2].copy_from_slice(&self.next_mgm_data.y.to_le_bytes()); + rx[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2].copy_from_slice(&self.next_mgm_data.z.to_le_bytes()); + self.call_count += 1; + } +} + pub struct SpiSimInterface { pub sim_request_tx: mpsc::Sender, pub sim_reply_rx: mpsc::Receiver, @@ -109,6 +132,8 @@ impl SpiSimInterface { pub enum SpiCommunication { Dummy(SpiDummyInterface), Sim(SpiSimInterface), + #[allow(dead_code)] + Test(TestSpiInterface), } impl SpiCommunication { @@ -116,6 +141,7 @@ impl SpiCommunication { match self { SpiCommunication::Dummy(dummy) => dummy.transfer(tx, rx), SpiCommunication::Sim(sim_if) => sim_if.transfer(tx, rx), + SpiCommunication::Test(test_if) => test_if.transfer(tx, rx), } } } @@ -135,34 +161,31 @@ pub struct ModeLeafHelper { /// Example MGM device handler strongly based on the LIS3MDL MEMS device. pub struct MgmHandlerLis3Mdl { id: MgmId, - tc_rx: mpsc::Receiver, - tm_tx: mpsc::SyncSender, switch_helper: PowerSwitchHelper, - pub com_interface: SpiCommunication, + tmtc_queues: TmtcQueues, + pub spi_com: SpiCommunication, shared_mgm_set: Arc>, buffers: BufWrapper, stamp_helper: TimestampHelper, hk_helper: HkHelperSingleSet, - mode_helpers: ModeHelper, + mode_helpers: ModeHelper, mode_leaf_helper: ModeLeafHelper, } impl MgmHandlerLis3Mdl { pub fn new( id: MgmId, - tc_rx: mpsc::Receiver, - tm_tx: mpsc::SyncSender, + tmtc_queues: TmtcQueues, switch_helper: PowerSwitchHelper, - com_interface: SpiCommunication, + spi_com: SpiCommunication, shared_mgm_set: Arc>, mode_leaf_helper: ModeLeafHelper, ) -> Self { Self { id, - tc_rx, - tm_tx, + tmtc_queues, switch_helper, - com_interface, + spi_com, shared_mgm_set, mode_helpers: ModeHelper::new(DeviceMode::Off, Duration::from_millis(200)), buffers: BufWrapper::default(), @@ -182,7 +205,6 @@ impl MgmHandlerLis3Mdl { match self.id { MgmId::_0 => SwitchId::Mgm0, MgmId::_1 => SwitchId::Mgm1, - _ => panic!("unexpected component id"), } } @@ -213,7 +235,7 @@ impl MgmHandlerLis3Mdl { pub fn handle_telecommands(&mut self) { loop { - match self.tc_rx.try_recv() { + match self.tmtc_queues.tc_rx.try_recv() { Ok(packet) => { let tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&packet.sp_header); match postcard::from_bytes::(&packet.payload) { @@ -275,7 +297,7 @@ impl MgmHandlerLis3Mdl { ) { match pack_ccsds_tm_packet_for_now(self.id.component_id(), tc_id, &response) { Ok(packet) => { - if let Err(e) = self.tm_tx.send(packet) { + if let Err(e) = self.tmtc_queues.tm_tx.send(packet) { log::warn!("failed to send TM packet: {}", e); } } @@ -319,12 +341,10 @@ impl MgmHandlerLis3Mdl { pub fn poll_sensor(&mut self) { // Communicate with the device. This is actually how to read the data from the LIS3 device // SPI interface. - self.com_interface - .transfer( - &self.buffers.tx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1], - &mut self.buffers.rx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1], - ) - .expect("failed to transfer data"); + self.spi_com.transfer( + &self.buffers.tx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1], + &mut self.buffers.rx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1], + ); let x_raw = i16::from_le_bytes( self.buffers.rx_buf[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2] .try_into() @@ -429,15 +449,16 @@ impl MgmHandlerLis3Mdl { #[cfg(test)] mod tests { use std::sync::{ - mpsc::{self, TryRecvError}, Arc, + mpsc::{self, TryRecvError}, }; use arbitrary_int::u11; use models::{ + Apid, ComponentId, TcHeader, + ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}, mgm::request::HkRequest, pcdu::{SwitchRequest, SwitchState, SwitchStateBinary}, - Apid, ComponentId, TcHeader, }; use satrs::{request::GenericMessage, spacepackets::SpacePacketHeader}; use satrs_minisim::acs::lis3mdl::MgmLis3RawValues; @@ -472,27 +493,6 @@ mod tests { ) } - #[derive(Default)] - pub struct TestSpiInterface { - pub call_count: u32, - pub next_mgm_data: MgmLis3RawValues, - } - - impl SpiCommunication for TestSpiInterface { - type Error = (); - - fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> { - rx[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2] - .copy_from_slice(&self.next_mgm_data.x.to_le_bytes()); - rx[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2] - .copy_from_slice(&self.next_mgm_data.y.to_le_bytes()); - rx[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2] - .copy_from_slice(&self.next_mgm_data.z.to_le_bytes()); - self.call_count += 1; - Ok(()) - } - } - #[allow(dead_code)] pub struct MgmTestbench { pub assembly_mode_request_tx: mpsc::SyncSender, @@ -501,7 +501,7 @@ mod tests { pub tc_tx: mpsc::SyncSender, pub tm_rx: mpsc::Receiver, pub switch_rx: mpsc::Receiver>, - pub handler: MgmHandlerLis3Mdl, + pub handler: MgmHandlerLis3Mdl, } impl MgmTestbench { @@ -522,10 +522,9 @@ mod tests { let shared_switch_set = SharedSwitchSet::new(Mutex::new(switch_map)); let handler = MgmHandlerLis3Mdl::new( MgmId::_0, - tc_rx, - tm_tx, + TmtcQueues { tc_rx, tm_tx }, PowerSwitchHelper::new(switcher_tx, shared_switch_set.clone()), - TestSpiInterface::default(), + SpiCommunication::Test(TestSpiInterface::default()), shared_mgm_set, mode_leaf_helper, ); @@ -539,16 +538,25 @@ mod tests { tc_tx, } } + + pub fn test_spi_interface(&mut self) -> &mut TestSpiInterface { + match &mut self.handler.spi_com { + SpiCommunication::Dummy(_) | SpiCommunication::Sim(_) => { + panic!("unexpected SPI interface") + } + SpiCommunication::Test(test_spi_interface) => test_spi_interface, + } + } } #[test] fn test_basic_handler() { let mut testbench = MgmTestbench::new(); - assert_eq!(testbench.handler.com_interface.call_count, 0); + assert_eq!(testbench.test_spi_interface().call_count, 0); assert_eq!(testbench.handler.mode(), DeviceMode::Off); testbench.handler.periodic_operation(); // Handler is OFF, no changes expected. - assert_eq!(testbench.handler.com_interface.call_count, 0); + assert_eq!(testbench.test_spi_interface().call_count, 0); assert_eq!(testbench.handler.mode(), DeviceMode::Off); } @@ -590,7 +598,7 @@ mod tests { .expect("failed to deserialize mode reply"); matches!(response, models::mgm::response::Response::Ok); // The device should have been polled once. - assert_eq!(testbench.handler.com_interface.call_count, 1); + assert_eq!(testbench.test_spi_interface().call_count, 1); let mgm_set = *testbench.handler.shared_mgm_set.lock().unwrap(); assert!(mgm_set.x < 0.001); assert!(mgm_set.y < 0.001); @@ -608,7 +616,7 @@ mod tests { y: -1000, z: 1000, }; - testbench.handler.com_interface.next_mgm_data = raw_values; + testbench.test_spi_interface().next_mgm_data = raw_values; testbench .tc_tx .send(create_request_tc( diff --git a/satrs-example/src/acs/mgm_assembly.rs b/satrs-example/src/acs/mgm_assembly.rs index 77cd5c6..538abdf 100644 --- a/satrs-example/src/acs/mgm_assembly.rs +++ b/satrs-example/src/acs/mgm_assembly.rs @@ -5,19 +5,20 @@ use std::{sync::mpsc, time::Duration}; use models::DeviceMode; -use satrs_example::ModeHelper; +use satrs_example::{ModeHelper, TmtcQueues}; pub enum ModeRequest { - SetMode(DeviceMode), + SetMode(AssemblyMode), ReadMode, } pub enum ModeReport { - Mode(DeviceMode), - /// Setting a mode timed out. - SetModeTimeout, - /// An assembly child lost the mode. - ChildLostMode, + /// Mode of the assembly. + Mode(AssemblyMode), + /// Failure setting the children mode. + SetModeRetryLimitReached([DeviceMode; 2]), + /// An assembly can not keep its mode. + CanNotKeepMode([DeviceMode; 2]), } pub struct ParentQueueHelper { @@ -31,34 +32,157 @@ pub struct ChildrenQueueHelper { pub report_rx_queues: [mpsc::Receiver; 2], } +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub enum TransitionState { + #[default] + Idle, + CommandingChildren { + step: usize, + }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AssemblyMode { + /// The assembly mode ressembles the modes of the devices it controls. It also tries to keep + /// the children in the correct mode by re-commanding them into the correct mode. + Device(DeviceMode), + /// Mode keeping disabled. + NoModeKeeping, +} + /// MGM assembly component. pub struct Assembly { - mode_helper: ModeHelper, + mode_helper: ModeHelper, + /// This boolean is used for the distinction between transitions commanded by the parent + /// or by ground, and transitions which were commanded autonomously as part of children + /// mode keeping. + mode_keeping_transition: bool, + tmtc_queues: TmtcQueues, + mgm_modes: [DeviceMode; 2], parent_queues: ParentQueueHelper, pub(crate) children_queues: ChildrenQueueHelper, } impl Assembly { - pub fn new(parent_queues: ParentQueueHelper, children_queues: ChildrenQueueHelper) -> Self { + const RETRIES: usize = 3; + + pub fn new( + parent_queues: ParentQueueHelper, + children_queues: ChildrenQueueHelper, + tmtc_queues: TmtcQueues, + ) -> Self { Self { - mode_helper: ModeHelper::new(DeviceMode::Unknown, Duration::from_millis(200)), + mode_helper: ModeHelper::new(AssemblyMode::NoModeKeeping, Duration::from_millis(200)), + mode_keeping_transition: false, + tmtc_queues, + mgm_modes: [DeviceMode::Off; 2], parent_queues, children_queues, } } pub fn periodic_operation(&mut self) { + self.handle_parent_mode_queue(); self.handle_children_mode_queues(); + + if self.mode_helper.transition_active() { + self.handle_mode_transition(); + } + } + + pub fn handle_mode_transition(&mut self) { + if self.mode_helper.transition_state == TransitionState::Idle { + self.mode_helper.transition_state = TransitionState::CommandingChildren { step: 0 }; + } + if let TransitionState::CommandingChildren { step } = self.mode_helper.transition_state + && self.mode_helper.timed_out() + { + if step >= Self::RETRIES { + let report = if self.mode_keeping_transition { + ModeReport::CanNotKeepMode(self.mgm_modes) + } else { + ModeReport::SetModeRetryLimitReached(self.mgm_modes) + }; + self.parent_queues.report_tx.send(report).unwrap(); + self.mode_helper.finish(false); + } + if let AssemblyMode::Device(device_mode) = self.mode_helper.target.unwrap() { + self.command_children(device_mode); + } else { + self.mode_helper.finish(true); + } + self.mode_helper.transition_state = + TransitionState::CommandingChildren { step: step + 1 } + } + } + + pub fn command_children(&self, mode: DeviceMode) { + for tx in &self.children_queues.request_tx_queues { + tx.send(super::mgm::ModeRequest::SetMode(mode)).unwrap(); + } + } + + pub fn handle_parent_mode_queue(&mut self) { + loop { + match self.parent_queues.request_rx.try_recv() { + Ok(request) => match request { + ModeRequest::SetMode(assembly_mode) => match assembly_mode { + AssemblyMode::Device(device_mode) => { + self.mode_keeping_transition = false; + self.mode_helper.start(AssemblyMode::Device(device_mode)); + } + AssemblyMode::NoModeKeeping => { + self.mode_helper.current = AssemblyMode::NoModeKeeping + } + }, + ModeRequest::ReadMode => self + .parent_queues + .report_tx + .send(ModeReport::Mode(self.mode_helper.current)) + .unwrap(), + }, + Err(e) => match e { + mpsc::TryRecvError::Empty => break, + mpsc::TryRecvError::Disconnected => { + log::warn!("packet sender disconnected") + } + }, + } + } } pub fn handle_children_mode_queues(&mut self) { - for rx in &mut self.children_queues.report_rx_queues { + for (idx, rx) in self.children_queues.report_rx_queues.iter_mut().enumerate() { loop { match rx.try_recv() { - // TODO: Do something with the report. Ok(report) => match report { - super::mgm::ModeReport::Mode(_device_mode) => todo!(), - super::mgm::ModeReport::SetModeTimeout => todo!(), + super::mgm::ModeReport::Mode(device_mode) => { + self.mgm_modes[idx] = device_mode; + + // Transition is active, check for completion. + // If at least one child reached the correct mode, we are done. + if self.mode_helper.transition_active() + && let AssemblyMode::Device(device_mode) = + self.mode_helper.target.unwrap() + && self.mgm_modes.contains(&device_mode) + { + self.mode_helper.finish(true); + } + + // Mode keeping active: Check children modes. + if let AssemblyMode::Device(device_mode) = self.mode_helper.current + && self.mgm_modes.iter().all(|m| *m != device_mode) + { + self.mode_keeping_transition = true; + // Children lost mode. Try to command them back to the correct + // mode. + self.mode_helper.start(self.mode_helper.current); + } + } + super::mgm::ModeReport::SetModeTimeout => { + // Ignore, handle this with our own timeout. + log::warn!("MGM {} mode timeout", idx); + } }, Err(e) => match e { mpsc::TryRecvError::Empty => break, diff --git a/satrs-example/src/ccsds.rs b/satrs-example/src/ccsds.rs index 39b6b14..7122809 100644 --- a/satrs-example/src/ccsds.rs +++ b/satrs-example/src/ccsds.rs @@ -1,8 +1,8 @@ use arbitrary_int::u11; -use models::{ccsds::CcsdsTmPacketOwned, Apid, ComponentId, Message, TmHeader}; +use models::{Apid, ComponentId, Message, TmHeader, ccsds::CcsdsTmPacketOwned}; use satrs::spacepackets::{ - time::{cds::CdsTime, StdTimestampError}, CcsdsPacketIdAndPsc, SpHeader, + time::{StdTimestampError, cds::CdsTime}, }; use serde::Serialize; diff --git a/satrs-example/src/control.rs b/satrs-example/src/control.rs index f27835b..0e74795 100644 --- a/satrs-example/src/control.rs +++ b/satrs-example/src/control.rs @@ -1,6 +1,7 @@ use models::{ + ComponentId, ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}, - control, ComponentId, + control, }; use satrs::spacepackets::CcsdsPacketIdAndPsc; diff --git a/satrs-example/src/eps/pcdu.rs b/satrs-example/src/eps/pcdu.rs index 5430019..9144490 100644 --- a/satrs-example/src/eps/pcdu.rs +++ b/satrs-example/src/eps/pcdu.rs @@ -1,24 +1,24 @@ use std::{ cell::RefCell, collections::{HashMap, VecDeque}, - sync::{mpsc, Arc, Mutex}, + sync::{Arc, Mutex, mpsc}, }; use derive_new::new; use models::{ + ComponentId, DeviceMode, ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}, pcdu::{ self, SwitchId, SwitchMapBinary, SwitchMapBinaryWrapper, SwitchRequest, SwitchState, SwitchStateBinary, SwitchesBitfield, }, - ComponentId, DeviceMode, }; use num_enum::{IntoPrimitive, TryFromPrimitive}; use satrs::{request::GenericMessage, spacepackets::CcsdsPacketIdAndPsc}; use satrs_example::TimestampHelper; use satrs_minisim::{ - eps::{PcduReply, PcduRequest}, SerializableSimMsgPayload, SimReply, SimRequest, + eps::{PcduReply, PcduRequest}, }; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator as _; @@ -532,8 +532,8 @@ mod tests { use arbitrary_int::u11; use models::{ - pcdu::{SwitchMapBinary, SwitchStateBinary}, Apid, TcHeader, + pcdu::{SwitchMapBinary, SwitchStateBinary}, }; use satrs::{ mode::{ModeReply, ModeRequest}, diff --git a/satrs-example/src/event_manager.rs b/satrs-example/src/event_manager.rs index a011ece..e5a129f 100644 --- a/satrs-example/src/event_manager.rs +++ b/satrs-example/src/event_manager.rs @@ -1,4 +1,4 @@ -use models::{ccsds::CcsdsTmPacketOwned, control, ComponentId, Event, Message}; +use models::{ComponentId, Event, Message, ccsds::CcsdsTmPacketOwned, control}; use crate::ccsds::pack_ccsds_tm_packet_for_now; diff --git a/satrs-example/src/interface/sim_client_udp.rs b/satrs-example/src/interface/sim_client_udp.rs index 3d8fa84..bec6db4 100644 --- a/satrs-example/src/interface/sim_client_udp.rs +++ b/satrs-example/src/interface/sim_client_udp.rs @@ -7,8 +7,8 @@ use std::{ use satrs::pus::HandlingStatus; use satrs_minisim::{ - udp::SIM_CTRL_PORT, SerializableSimMsgPayload, SimComponent, SimMessageProvider, SimReply, - SimRequest, + SerializableSimMsgPayload, SimComponent, SimMessageProvider, SimReply, SimRequest, + udp::SIM_CTRL_PORT, }; use satrs_minisim::{SimCtrlReply, SimCtrlRequest}; @@ -187,16 +187,17 @@ pub mod tests { collections::HashMap, net::{SocketAddr, UdpSocket}, sync::{ + Arc, atomic::{AtomicBool, Ordering}, - mpsc, Arc, + mpsc, }, time::Duration, }; use satrs_minisim::{ - eps::{PcduReply, PcduRequest}, SerializableSimMsgPayload, SimComponent, SimCtrlReply, SimCtrlRequest, SimMessageProvider, SimReply, SimRequest, + eps::{PcduReply, PcduRequest}, }; use super::SimClientUdp; diff --git a/satrs-example/src/interface/udp.rs b/satrs-example/src/interface/udp.rs index 6a0395d..350a740 100644 --- a/satrs-example/src/interface/udp.rs +++ b/satrs-example/src/interface/udp.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use std::collections::VecDeque; use std::net::{SocketAddr, UdpSocket}; -use std::sync::{mpsc, Arc, Mutex}; +use std::sync::{Arc, Mutex, mpsc}; use log::warn; use models::ccsds::CcsdsTmPacketOwned; @@ -115,11 +115,11 @@ mod tests { use models::Apid; use satrs::spacepackets::ecss::{CreatorConfig, MessageTypeId}; use satrs::{ - spacepackets::{ - ecss::{tc::PusTcCreator, WritablePusPacket}, - SpHeader, - }, ComponentId, + spacepackets::{ + SpHeader, + ecss::{WritablePusPacket, tc::PusTcCreator}, + }, }; use satrs_example::config::OBSW_SERVER_ADDR; diff --git a/satrs-example/src/lib.rs b/satrs-example/src/lib.rs index 06aef60..97cf771 100644 --- a/satrs-example/src/lib.rs +++ b/satrs-example/src/lib.rs @@ -1,9 +1,13 @@ extern crate alloc; -use std::time::{Duration, Instant}; +use std::{ + sync::mpsc, + time::{Duration, Instant}, +}; pub use models::ComponentId; -use satrs::spacepackets::{time::cds::CdsTime, CcsdsPacketIdAndPsc}; +use models::ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}; +use satrs::spacepackets::{CcsdsPacketIdAndPsc, time::cds::CdsTime}; pub mod config; @@ -91,16 +95,13 @@ impl HkHelperSingleSet { } } -#[derive(Default, Debug, PartialEq, Eq)] -pub enum TransitionState { - #[default] - Idle, - PowerSwitching, - Done, +pub struct TmtcQueues { + pub tc_rx: mpsc::Receiver, + pub tm_tx: mpsc::SyncSender, } #[derive(Debug)] -pub struct ModeHelper { +pub struct ModeHelper { pub current: Mode, pub target: Option, pub tc_id: Option, @@ -109,7 +110,7 @@ pub struct ModeHelper { pub transition_state: TransitionState, } -impl ModeHelper { +impl ModeHelper { pub fn new(init_mode: Mode, timeout: Duration) -> Self { Self { current: init_mode, @@ -124,7 +125,12 @@ impl ModeHelper { pub fn start(&mut self, target: Mode) { self.target = Some(target); self.transition_start = Some(Instant::now()); - self.transition_state = TransitionState::Idle; + self.transition_state = TransitionState::default(); + } + + #[inline] + pub fn transition_active(&self) -> bool { + self.target.is_some() } pub fn timed_out(&self) -> bool { @@ -146,6 +152,7 @@ impl ModeHelper { } else { self.target = None; } + self.transition_state = Default::default(); self.transition_start = None; } } diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index bedec10..2302941 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -1,13 +1,13 @@ use std::{ net::{IpAddr, SocketAddr}, - sync::{mpsc, Arc, Mutex}, + sync::{Arc, Mutex, mpsc}, thread, time::Duration, }; use eps::{ - pcdu::{PcduHandler, SerialInterfaceDummy, SerialInterfaceToSim, SerialSimInterfaceWrapper}, PowerSwitchHelper, + pcdu::{PcduHandler, SerialInterfaceDummy, SerialInterfaceToSim, SerialSimInterfaceWrapper}, }; use interface::{ sim_client_udp::create_sim_client, @@ -24,10 +24,13 @@ use satrs::{ request::{GenericMessage, MessageMetadata}, spacepackets::time::cds::CdsTime, }; -use satrs_example::config::{ - components::NO_SENDER, - tasks::{FREQ_MS_AOCS, FREQ_MS_CONTROLLER, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS}, - OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT, +use satrs_example::{ + TmtcQueues, + config::{ + OBSW_SERVER_ADDR, PACKET_ID_VALIDATOR, SERVER_PORT, + components::NO_SENDER, + tasks::{FREQ_MS_AOCS, FREQ_MS_CONTROLLER, FREQ_MS_UDP_TMTC, SIM_CLIENT_IDLE_DELAY_MS}, + }, }; use tmtc::sender::TmTcSender; use tmtc::{tc_source::TcSourceTask, tm_sink::TmSink}; @@ -66,6 +69,7 @@ fn main() { let (mgm_0_handler_tc_tx, mgm_0_handler_tc_rx) = mpsc::sync_channel(10); let (mgm_1_handler_tc_tx, mgm_1_handler_tc_rx) = mpsc::sync_channel(10); + let (_mgm_assembly_tc_tx, mgm_assembly_tc_rx) = mpsc::sync_channel(10); let (pcdu_handler_tc_tx, pcdu_handler_tc_rx) = mpsc::sync_channel(30); let (controller_tc_tx, controller_tc_rx) = mpsc::sync_channel(10); @@ -145,25 +149,27 @@ fn main() { sim_client .add_reply_recipient(satrs_minisim::SimComponent::Mgm1Lis3Mdl, mgm_1_sim_reply_tx); ( - mgm::SpiInterface::Sim(mgm::SpiSimInterface { + mgm::SpiCommunication::Sim(mgm::SpiSimInterface { sim_request_tx: sim_request_tx.clone(), sim_reply_rx: mgm_0_sim_reply_rx, }), - mgm::SpiInterface::Sim(mgm::SpiSimInterface { + mgm::SpiCommunication::Sim(mgm::SpiSimInterface { sim_request_tx: sim_request_tx.clone(), sim_reply_rx: mgm_1_sim_reply_rx, }), ) } else { ( - mgm::SpiInterface::Dummy(mgm::SpiDummyInterface::default()), - mgm::SpiInterface::Dummy(mgm::SpiDummyInterface::default()), + mgm::SpiCommunication::Dummy(mgm::SpiDummyInterface::default()), + mgm::SpiCommunication::Dummy(mgm::SpiDummyInterface::default()), ) }; let mut mgm_0_handler = mgm::MgmHandlerLis3Mdl::new( mgm::MgmId::_0, - mgm_0_handler_tc_rx, - tm_sink_tx.clone(), + TmtcQueues { + tc_rx: mgm_0_handler_tc_rx, + tm_tx: tm_sink_tx.clone(), + }, switch_helper.clone(), mgm_0_spi_interface, shared_mgm_0_set, @@ -174,8 +180,10 @@ fn main() { ); let mut mgm_1_handler = mgm::MgmHandlerLis3Mdl::new( mgm::MgmId::_1, - mgm_1_handler_tc_rx, - tm_sink_tx.clone(), + TmtcQueues { + tc_rx: mgm_1_handler_tc_rx, + tm_tx: tm_sink_tx.clone(), + }, switch_helper.clone(), mgm_1_spi_interface, shared_mgm_1_set, @@ -193,6 +201,10 @@ fn main() { request_tx_queues: [mgm_0_mode_request_tx, mgm_1_mode_request_tx], report_rx_queues: [mgm_0_mode_report_rx, mgm_1_mode_report_rx], }, + TmtcQueues { + tc_rx: mgm_assembly_tc_rx, + tm_tx: tm_sink_tx.clone(), + }, ); let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() { @@ -251,8 +263,10 @@ fn main() { info!("Starting TM funnel task"); let jh_tm_funnel = thread::Builder::new() .name("TM SINK".to_string()) - .spawn(move || loop { - tm_sink.operation(); + .spawn(move || { + loop { + tm_sink.operation(); + } }) .unwrap(); @@ -262,9 +276,11 @@ fn main() { opt_jh_sim_client = Some( thread::Builder::new() .name("SIM ADAPTER".to_string()) - .spawn(move || loop { - if sim_client.operation() == HandlingStatus::Empty { - std::thread::sleep(Duration::from_millis(SIM_CLIENT_IDLE_DELAY_MS)); + .spawn(move || { + loop { + if sim_client.operation() == HandlingStatus::Empty { + std::thread::sleep(Duration::from_millis(SIM_CLIENT_IDLE_DELAY_MS)); + } } }) .unwrap(), @@ -274,39 +290,45 @@ fn main() { info!("Starting AOCS thread"); let jh_aocs = thread::Builder::new() .name("AOCS".to_string()) - .spawn(move || loop { - mgm_0_handler.periodic_operation(); - mgm_1_handler.periodic_operation(); - mgm_assembly.periodic_operation(); - thread::sleep(Duration::from_millis(FREQ_MS_AOCS)); + .spawn(move || { + loop { + mgm_0_handler.periodic_operation(); + mgm_1_handler.periodic_operation(); + mgm_assembly.periodic_operation(); + thread::sleep(Duration::from_millis(FREQ_MS_AOCS)); + } }) .unwrap(); info!("Starting EPS thread"); let jh_eps = thread::Builder::new() .name("EPS".to_string()) - .spawn(move || loop { - // TODO: We should introduce something like a fixed timeslot helper to allow a more - // declarative API. It would also be very useful for the AOCS task. - // - // TODO: The fixed timeslot handler exists.. use it. - // TODO: Why not just use sync code in the PCDU handler, and fully delay there? - pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::RegularOp); - thread::sleep(Duration::from_millis(50)); - pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::PollAndRecvReplies); - thread::sleep(Duration::from_millis(50)); - pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::PollAndRecvReplies); - thread::sleep(Duration::from_millis(300)); + .spawn(move || { + loop { + // TODO: We should introduce something like a fixed timeslot helper to allow a more + // declarative API. It would also be very useful for the AOCS task. + // + // TODO: The fixed timeslot handler exists.. use it. + // TODO: Why not just use sync code in the PCDU handler, and fully delay there? + pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::RegularOp); + thread::sleep(Duration::from_millis(50)); + pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::PollAndRecvReplies); + thread::sleep(Duration::from_millis(50)); + pcdu_handler.periodic_operation(crate::eps::pcdu::OpCode::PollAndRecvReplies); + thread::sleep(Duration::from_millis(300)); + } }) .unwrap(); info!("Starting controller thread"); let jh_controller_thread = thread::Builder::new() .name("CTRL".to_string()) - .spawn(move || loop { - controller.periodic_operation(); - event_manager.periodic_operation(); - thread::sleep(Duration::from_millis(FREQ_MS_CONTROLLER)); + .spawn(move || { + loop { + controller.periodic_operation(); + event_manager.periodic_operation(); + thread::sleep(Duration::from_millis(FREQ_MS_CONTROLLER)); + } }) .unwrap(); diff --git a/satrs-example/src/tmtc/sender.rs b/satrs-example/src/tmtc/sender.rs index 71fd17b..26f84e4 100644 --- a/satrs-example/src/tmtc/sender.rs +++ b/satrs-example/src/tmtc/sender.rs @@ -1,9 +1,9 @@ use std::{cell::RefCell, collections::VecDeque, sync::mpsc}; use satrs::{ + ComponentId, queue::GenericSendError, tmtc::{PacketAsVec, PacketHandler}, - ComponentId, }; #[derive(Default, Debug, Clone)] diff --git a/satrs-example/src/tmtc/tc_source.rs b/satrs-example/src/tmtc/tc_source.rs index 6a26a40..a20af6a 100644 --- a/satrs-example/src/tmtc/tc_source.rs +++ b/satrs-example/src/tmtc/tc_source.rs @@ -1,4 +1,4 @@ -use models::{ccsds::CcsdsTcPacketOwned, ComponentId, TcHeader}; +use models::{ComponentId, TcHeader, ccsds::CcsdsTcPacketOwned}; use satrs::{ pus::HandlingStatus, spacepackets::{CcsdsPacketReader, ChecksumType},