continue integrating power subsystem
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
This commit is contained in:
parent
27e88ed7f7
commit
fe60cb9ccf
@ -1,5 +1,6 @@
|
|||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use satrs::hk::{HkRequest, HkRequestVariant};
|
use satrs::hk::{HkRequest, HkRequestVariant};
|
||||||
|
use satrs::power::{PowerSwitchInfo, PowerSwitcherCommandSender};
|
||||||
use satrs::queue::{GenericSendError, GenericTargetedMessagingError};
|
use satrs::queue::{GenericSendError, GenericTargetedMessagingError};
|
||||||
use satrs::spacepackets::ecss::hk;
|
use satrs::spacepackets::ecss::hk;
|
||||||
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||||
@ -9,6 +10,7 @@ use satrs_minisim::acs::lis3mdl::{
|
|||||||
MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR,
|
MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR,
|
||||||
};
|
};
|
||||||
use satrs_minisim::acs::MgmRequestLis3Mdl;
|
use satrs_minisim::acs::MgmRequestLis3Mdl;
|
||||||
|
use satrs_minisim::eps::PcduSwitch;
|
||||||
use satrs_minisim::{SerializableSimMsgPayload, SimReply, SimRequest};
|
use satrs_minisim::{SerializableSimMsgPayload, SimReply, SimRequest};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::mpsc::{self};
|
use std::sync::mpsc::{self};
|
||||||
@ -127,31 +129,44 @@ pub struct MpscModeLeafInterface {
|
|||||||
pub reply_to_parent_tx: mpsc::Sender<GenericMessage<ModeReply>>,
|
pub reply_to_parent_tx: mpsc::Sender<GenericMessage<ModeReply>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BufWrapper {
|
||||||
|
tx_buf: [u8; 32],
|
||||||
|
rx_buf: [u8; 32],
|
||||||
|
tm_buf: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
/// Example MGM device handler strongly based on the LIS3MDL MEMS device.
|
/// Example MGM device handler strongly based on the LIS3MDL MEMS device.
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub struct MgmHandlerLis3Mdl<ComInterface: SpiInterface, TmSender: EcssTmSender> {
|
pub struct MgmHandlerLis3Mdl<
|
||||||
|
ComInterface: SpiInterface,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
||||||
|
> {
|
||||||
id: UniqueApidTargetId,
|
id: UniqueApidTargetId,
|
||||||
dev_str: &'static str,
|
dev_str: &'static str,
|
||||||
mode_interface: MpscModeLeafInterface,
|
mode_interface: MpscModeLeafInterface,
|
||||||
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
composite_request_rx: mpsc::Receiver<GenericMessage<CompositeRequest>>,
|
||||||
hk_reply_tx: mpsc::Sender<GenericMessage<HkReply>>,
|
hk_reply_tx: mpsc::Sender<GenericMessage<HkReply>>,
|
||||||
|
switch_helper: SwitchHelper,
|
||||||
tm_sender: TmSender,
|
tm_sender: TmSender,
|
||||||
pub com_interface: ComInterface,
|
pub com_interface: ComInterface,
|
||||||
shared_mgm_set: Arc<Mutex<MgmData>>,
|
shared_mgm_set: Arc<Mutex<MgmData>>,
|
||||||
#[new(value = "ModeAndSubmode::new(satrs_example::DeviceMode::Off as u32, 0)")]
|
#[new(value = "ModeAndSubmode::new(satrs_example::DeviceMode::Off as u32, 0)")]
|
||||||
mode_and_submode: ModeAndSubmode,
|
mode_and_submode: ModeAndSubmode,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
tx_buf: [u8; 32],
|
bufs: BufWrapper,
|
||||||
#[new(default)]
|
|
||||||
rx_buf: [u8; 32],
|
|
||||||
#[new(default)]
|
|
||||||
tm_buf: [u8; 32],
|
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
stamp_helper: TimestampHelper,
|
stamp_helper: TimestampHelper,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<ComInterface: SpiInterface, TmSender: EcssTmSender> MgmHandlerLis3Mdl<ComInterface, TmSender> {
|
impl<
|
||||||
|
ComInterface: SpiInterface,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
||||||
|
> MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
|
||||||
|
{
|
||||||
pub fn periodic_operation(&mut self) {
|
pub fn periodic_operation(&mut self) {
|
||||||
self.stamp_helper.update_from_now();
|
self.stamp_helper.update_from_now();
|
||||||
// Handle requests.
|
// Handle requests.
|
||||||
@ -210,17 +225,17 @@ impl<ComInterface: SpiInterface, TmSender: EcssTmSender> MgmHandlerLis3Mdl<ComIn
|
|||||||
self.stamp_helper.stamp(),
|
self.stamp_helper.stamp(),
|
||||||
);
|
);
|
||||||
let mgm_snapshot = *self.shared_mgm_set.lock().unwrap();
|
let mgm_snapshot = *self.shared_mgm_set.lock().unwrap();
|
||||||
self.tm_buf[0..4].copy_from_slice(&self.id.unique_id.to_be_bytes());
|
self.bufs.tm_buf[0..4].copy_from_slice(&self.id.unique_id.to_be_bytes());
|
||||||
self.tm_buf[4..8].copy_from_slice(&(SetId::SensorData as u32).to_be_bytes());
|
self.bufs.tm_buf[4..8].copy_from_slice(&(SetId::SensorData as u32).to_be_bytes());
|
||||||
// Use binary serialization here. We want the data to be tightly packed.
|
// Use binary serialization here. We want the data to be tightly packed.
|
||||||
self.tm_buf[8] = mgm_snapshot.valid as u8;
|
self.bufs.tm_buf[8] = mgm_snapshot.valid as u8;
|
||||||
self.tm_buf[9..13].copy_from_slice(&mgm_snapshot.x.to_be_bytes());
|
self.bufs.tm_buf[9..13].copy_from_slice(&mgm_snapshot.x.to_be_bytes());
|
||||||
self.tm_buf[13..17].copy_from_slice(&mgm_snapshot.y.to_be_bytes());
|
self.bufs.tm_buf[13..17].copy_from_slice(&mgm_snapshot.y.to_be_bytes());
|
||||||
self.tm_buf[17..21].copy_from_slice(&mgm_snapshot.z.to_be_bytes());
|
self.bufs.tm_buf[17..21].copy_from_slice(&mgm_snapshot.z.to_be_bytes());
|
||||||
let hk_tm = PusTmCreator::new(
|
let hk_tm = PusTmCreator::new(
|
||||||
SpHeader::new_from_apid(self.id.apid),
|
SpHeader::new_from_apid(self.id.apid),
|
||||||
sec_header,
|
sec_header,
|
||||||
&self.tm_buf[0..21],
|
&self.bufs.tm_buf[0..21],
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
self.tm_sender
|
self.tm_sender
|
||||||
@ -264,22 +279,22 @@ impl<ComInterface: SpiInterface, TmSender: EcssTmSender> MgmHandlerLis3Mdl<ComIn
|
|||||||
// SPI interface.
|
// SPI interface.
|
||||||
self.com_interface
|
self.com_interface
|
||||||
.transfer(
|
.transfer(
|
||||||
&self.tx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1],
|
&self.bufs.tx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1],
|
||||||
&mut self.rx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1],
|
&mut self.bufs.rx_buf[0..NR_OF_DATA_AND_CFG_REGISTERS + 1],
|
||||||
)
|
)
|
||||||
.expect("failed to transfer data");
|
.expect("failed to transfer data");
|
||||||
let x_raw = i16::from_le_bytes(
|
let x_raw = i16::from_le_bytes(
|
||||||
self.rx_buf[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2]
|
self.bufs.rx_buf[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2]
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
let y_raw = i16::from_le_bytes(
|
let y_raw = i16::from_le_bytes(
|
||||||
self.rx_buf[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2]
|
self.bufs.rx_buf[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2]
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
let z_raw = i16::from_le_bytes(
|
let z_raw = i16::from_le_bytes(
|
||||||
self.rx_buf[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2]
|
self.bufs.rx_buf[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2]
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
@ -293,16 +308,22 @@ impl<ComInterface: SpiInterface, TmSender: EcssTmSender> MgmHandlerLis3Mdl<ComIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<ComInterface: SpiInterface, TmSender: EcssTmSender> ModeProvider
|
impl<
|
||||||
for MgmHandlerLis3Mdl<ComInterface, TmSender>
|
ComInterface: SpiInterface,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
||||||
|
> ModeProvider for MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
|
||||||
{
|
{
|
||||||
fn mode_and_submode(&self) -> ModeAndSubmode {
|
fn mode_and_submode(&self) -> ModeAndSubmode {
|
||||||
self.mode_and_submode
|
self.mode_and_submode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<ComInterface: SpiInterface, TmSender: EcssTmSender> ModeRequestHandler
|
impl<
|
||||||
for MgmHandlerLis3Mdl<ComInterface, TmSender>
|
ComInterface: SpiInterface,
|
||||||
|
TmSender: EcssTmSender,
|
||||||
|
SwitchHelper: PowerSwitchInfo<PcduSwitch> + PowerSwitcherCommandSender<PcduSwitch>,
|
||||||
|
> ModeRequestHandler for MgmHandlerLis3Mdl<ComInterface, TmSender, SwitchHelper>
|
||||||
{
|
{
|
||||||
type Error = ModeError;
|
type Error = ModeError;
|
||||||
fn start_transition(
|
fn start_transition(
|
||||||
@ -388,17 +409,17 @@ mod tests {
|
|||||||
use satrs_example::config::components::Apid;
|
use satrs_example::config::components::Apid;
|
||||||
use satrs_minisim::acs::lis3mdl::MgmLis3RawValues;
|
use satrs_minisim::acs::lis3mdl::MgmLis3RawValues;
|
||||||
|
|
||||||
use crate::{pus::hk::HkReply, requests::CompositeRequest};
|
use crate::{eps::TestSwitchHelper, pus::hk::HkReply, requests::CompositeRequest};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TestInterface {
|
pub struct TestSpiInterface {
|
||||||
pub call_count: u32,
|
pub call_count: u32,
|
||||||
pub next_mgm_data: MgmLis3RawValues,
|
pub next_mgm_data: MgmLis3RawValues,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpiInterface for TestInterface {
|
impl SpiInterface for TestSpiInterface {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
|
fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
@ -420,7 +441,8 @@ mod tests {
|
|||||||
pub composite_request_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
|
pub composite_request_tx: mpsc::Sender<GenericMessage<CompositeRequest>>,
|
||||||
pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>,
|
pub hk_reply_rx: mpsc::Receiver<GenericMessage<HkReply>>,
|
||||||
pub tm_rx: mpsc::Receiver<PacketAsVec>,
|
pub tm_rx: mpsc::Receiver<PacketAsVec>,
|
||||||
pub handler: MgmHandlerLis3Mdl<TestInterface, mpsc::Sender<PacketAsVec>>,
|
pub handler:
|
||||||
|
MgmHandlerLis3Mdl<TestSpiInterface, mpsc::Sender<PacketAsVec>, TestSwitchHelper>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MgmTestbench {
|
impl MgmTestbench {
|
||||||
@ -450,8 +472,9 @@ mod tests {
|
|||||||
mode_interface,
|
mode_interface,
|
||||||
composite_request_rx,
|
composite_request_rx,
|
||||||
hk_reply_tx,
|
hk_reply_tx,
|
||||||
|
TestSwitchHelper::default(),
|
||||||
tm_tx,
|
tm_tx,
|
||||||
TestInterface::default(),
|
TestSpiInterface::default(),
|
||||||
shared_mgm_set,
|
shared_mgm_set,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -1 +1,204 @@
|
|||||||
|
use derive_new::new;
|
||||||
|
use std::{
|
||||||
|
borrow::BorrowMut,
|
||||||
|
cell::RefCell,
|
||||||
|
collections::VecDeque,
|
||||||
|
sync::mpsc,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use satrs::{
|
||||||
|
power::{
|
||||||
|
PowerSwitchInfo, PowerSwitcherCommandSender, SwitchId, SwitchRequest, SwitchState,
|
||||||
|
SwitchStateBinary,
|
||||||
|
},
|
||||||
|
queue::GenericSendError,
|
||||||
|
request::{GenericMessage, MessageMetadata},
|
||||||
|
};
|
||||||
|
use satrs_minisim::eps::{PcduSwitch, SwitchMapWrapper};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use self::pcdu::SharedSwitchSet;
|
||||||
|
|
||||||
pub mod pcdu;
|
pub mod pcdu;
|
||||||
|
|
||||||
|
#[derive(new, Clone)]
|
||||||
|
pub struct PowerSwitchHelper {
|
||||||
|
switcher_tx: mpsc::SyncSender<GenericMessage<SwitchRequest>>,
|
||||||
|
shared_switch_set: SharedSwitchSet,
|
||||||
|
#[new(default)]
|
||||||
|
switch_cmd_sent_instant: Option<Instant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum SwitchCommandingError {
|
||||||
|
#[error("invalid switch id")]
|
||||||
|
InvalidSwitchId(SwitchId),
|
||||||
|
#[error("send error: {0}")]
|
||||||
|
Send(#[from] GenericSendError),
|
||||||
|
}
|
||||||
|
#[derive(Debug, Error, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum SwitchInfoError {
|
||||||
|
/// This is a configuration error which should not occur.
|
||||||
|
#[error("switch ID not in map")]
|
||||||
|
SwitchIdNotInMap(PcduSwitch),
|
||||||
|
#[error("switch set invalid")]
|
||||||
|
SwitchSetInvalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerSwitchInfo<PcduSwitch> for PowerSwitchHelper {
|
||||||
|
type Error = SwitchInfoError;
|
||||||
|
|
||||||
|
fn switch_state(
|
||||||
|
&self,
|
||||||
|
switch_id: PcduSwitch,
|
||||||
|
) -> Result<satrs::power::SwitchState, Self::Error> {
|
||||||
|
let switch_set = self
|
||||||
|
.shared_switch_set
|
||||||
|
.lock()
|
||||||
|
.expect("failed to lock switch set");
|
||||||
|
if !switch_set.valid {
|
||||||
|
return Err(SwitchInfoError::SwitchSetInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(state) = switch_set.switch_map.get(&switch_id) {
|
||||||
|
return Ok(*state);
|
||||||
|
}
|
||||||
|
Err(SwitchInfoError::SwitchIdNotInMap(switch_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn switch_delay_ms(&self) -> Duration {
|
||||||
|
// Here, we could set device specific switch delays theoretically. Set it to this value
|
||||||
|
// for now.
|
||||||
|
Duration::from_millis(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerSwitcherCommandSender<PcduSwitch> for PowerSwitchHelper {
|
||||||
|
type Error = SwitchCommandingError;
|
||||||
|
|
||||||
|
fn send_switch_on_cmd(
|
||||||
|
&self,
|
||||||
|
requestor_info: satrs::request::MessageMetadata,
|
||||||
|
switch_id: PcduSwitch,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.switcher_tx
|
||||||
|
.send_switch_on_cmd(requestor_info, switch_id)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_switch_off_cmd(
|
||||||
|
&self,
|
||||||
|
requestor_info: satrs::request::MessageMetadata,
|
||||||
|
switch_id: PcduSwitch,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.switcher_tx
|
||||||
|
.send_switch_off_cmd(requestor_info, switch_id)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(new)]
|
||||||
|
pub struct SwitchRequestInfo {
|
||||||
|
pub requestor_info: MessageMetadata,
|
||||||
|
pub switch_id: PcduSwitch,
|
||||||
|
pub target_state: satrs::power::SwitchStateBinary,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test switch helper which can be used for unittests.
|
||||||
|
pub struct TestSwitchHelper {
|
||||||
|
pub switch_requests: RefCell<VecDeque<SwitchRequestInfo>>,
|
||||||
|
pub switch_info_requests: RefCell<VecDeque<PcduSwitch>>,
|
||||||
|
pub switch_delay_request_count: u32,
|
||||||
|
pub next_switch_delay: Duration,
|
||||||
|
pub switch_map: RefCell<SwitchMapWrapper>,
|
||||||
|
pub switch_map_valid: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TestSwitchHelper {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
switch_requests: Default::default(),
|
||||||
|
switch_info_requests: Default::default(),
|
||||||
|
switch_delay_request_count: Default::default(),
|
||||||
|
next_switch_delay: Duration::from_millis(1000),
|
||||||
|
switch_map: Default::default(),
|
||||||
|
switch_map_valid: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerSwitchInfo<PcduSwitch> for TestSwitchHelper {
|
||||||
|
type Error = SwitchInfoError;
|
||||||
|
|
||||||
|
fn switch_state(
|
||||||
|
&self,
|
||||||
|
switch_id: PcduSwitch,
|
||||||
|
) -> Result<satrs::power::SwitchState, Self::Error> {
|
||||||
|
let mut switch_info_requests_mut = self.switch_info_requests.borrow_mut();
|
||||||
|
switch_info_requests_mut.push_back(switch_id);
|
||||||
|
if !self.switch_map_valid {
|
||||||
|
return Err(SwitchInfoError::SwitchSetInvalid);
|
||||||
|
}
|
||||||
|
let switch_map_mut = self.switch_map.borrow_mut();
|
||||||
|
if let Some(state) = switch_map_mut.0.get(&switch_id) {
|
||||||
|
return Ok(*state);
|
||||||
|
}
|
||||||
|
Err(SwitchInfoError::SwitchIdNotInMap(switch_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn switch_delay_ms(&self) -> Duration {
|
||||||
|
self.next_switch_delay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerSwitcherCommandSender<PcduSwitch> for TestSwitchHelper {
|
||||||
|
type Error = SwitchCommandingError;
|
||||||
|
|
||||||
|
fn send_switch_on_cmd(
|
||||||
|
&self,
|
||||||
|
requestor_info: MessageMetadata,
|
||||||
|
switch_id: PcduSwitch,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let mut switch_requests_mut = self.switch_requests.borrow_mut();
|
||||||
|
switch_requests_mut.push_back(SwitchRequestInfo {
|
||||||
|
requestor_info,
|
||||||
|
switch_id,
|
||||||
|
target_state: SwitchStateBinary::On,
|
||||||
|
});
|
||||||
|
// By default, the test helper immediately acknowledges the switch request by setting
|
||||||
|
// the appropriate switch state in the internal switch map.
|
||||||
|
let mut switch_map_mut = self.switch_map.borrow_mut();
|
||||||
|
if let Some(switch_state) = switch_map_mut.0.get_mut(&switch_id) {
|
||||||
|
*switch_state = SwitchState::On;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_switch_off_cmd(
|
||||||
|
&self,
|
||||||
|
requestor_info: MessageMetadata,
|
||||||
|
switch_id: PcduSwitch,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let mut switch_requests_mut = self.switch_requests.borrow_mut();
|
||||||
|
switch_requests_mut.push_back(SwitchRequestInfo {
|
||||||
|
requestor_info,
|
||||||
|
switch_id,
|
||||||
|
target_state: SwitchStateBinary::Off,
|
||||||
|
});
|
||||||
|
// By default, the test helper immediately acknowledges the switch request by setting
|
||||||
|
// the appropriate switch state in the internal switch map.
|
||||||
|
let mut switch_map_mut = self.switch_map.borrow_mut();
|
||||||
|
if let Some(switch_state) = switch_map_mut.0.get_mut(&switch_id) {
|
||||||
|
*switch_state = SwitchState::Off;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestSwitchHelper {
|
||||||
|
// Helper function which can be used to force a switch to another state for test purposes.
|
||||||
|
pub fn set_switch_state(&mut self, switch: PcduSwitch, state: SwitchState) {
|
||||||
|
self.switch_map.get_mut().0.insert(switch, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,14 +8,14 @@ use derive_new::new;
|
|||||||
use satrs::{
|
use satrs::{
|
||||||
hk::{HkRequest, HkRequestVariant},
|
hk::{HkRequest, HkRequestVariant},
|
||||||
mode::{ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequestHandler},
|
mode::{ModeAndSubmode, ModeError, ModeProvider, ModeReply, ModeRequestHandler},
|
||||||
power::{SwitchRequest, SwitchStateBinary},
|
power::SwitchRequest,
|
||||||
pus::EcssTmSender,
|
pus::EcssTmSender,
|
||||||
queue::{GenericSendError, GenericTargetedMessagingError},
|
queue::{GenericSendError, GenericTargetedMessagingError},
|
||||||
request::{GenericMessage, MessageMetadata, UniqueApidTargetId},
|
request::{GenericMessage, MessageMetadata, UniqueApidTargetId},
|
||||||
};
|
};
|
||||||
use satrs_example::{config::components::PUS_MODE_SERVICE, DeviceMode, TimestampHelper};
|
use satrs_example::{config::components::PUS_MODE_SERVICE, DeviceMode, TimestampHelper};
|
||||||
use satrs_minisim::{
|
use satrs_minisim::{
|
||||||
eps::{PcduReply, PcduRequest, PcduSwitch, SwitchMap, SwitchMapBinary, SwitchMapBinaryWrapper},
|
eps::{PcduReply, PcduRequest, PcduSwitch, SwitchMap, SwitchMapBinaryWrapper},
|
||||||
SerializableSimMsgPayload, SimReply, SimRequest,
|
SerializableSimMsgPayload, SimReply, SimRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ mod tmtc;
|
|||||||
use crate::eps::pcdu::{
|
use crate::eps::pcdu::{
|
||||||
PcduHandler, SerialInterfaceDummy, SerialInterfaceToSim, SerialSimInterfaceWrapper,
|
PcduHandler, SerialInterfaceDummy, SerialInterfaceToSim, SerialSimInterfaceWrapper,
|
||||||
};
|
};
|
||||||
|
use crate::eps::PowerSwitchHelper;
|
||||||
use crate::events::EventHandler;
|
use crate::events::EventHandler;
|
||||||
use crate::interface::udp::DynamicUdpTmHandler;
|
use crate::interface::udp::DynamicUdpTmHandler;
|
||||||
use crate::pus::stack::PusStack;
|
use crate::pus::stack::PusStack;
|
||||||
@ -50,7 +51,7 @@ use satrs::pus::event_man::EventRequestWithToken;
|
|||||||
use satrs::spacepackets::{time::cds::CdsTime, time::TimeWriter};
|
use satrs::spacepackets::{time::cds::CdsTime, time::TimeWriter};
|
||||||
use satrs_example::config::components::{MGM_HANDLER_0, PCDU_HANDLER, TCP_SERVER, UDP_SERVER};
|
use satrs_example::config::components::{MGM_HANDLER_0, PCDU_HANDLER, TCP_SERVER, UDP_SERVER};
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::sync::mpsc;
|
use std::sync::{mpsc, Mutex};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -222,8 +223,9 @@ fn static_tmtc_pool_main() {
|
|||||||
let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
|
let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
|
||||||
mpsc::channel();
|
mpsc::channel();
|
||||||
|
|
||||||
let shared_switch_set = Arc::default();
|
let shared_switch_set = Arc::new(Mutex::default());
|
||||||
let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20);
|
let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20);
|
||||||
|
let switch_helper = PowerSwitchHelper::new(switch_request_tx, shared_switch_set.clone());
|
||||||
|
|
||||||
let shared_mgm_set = Arc::default();
|
let shared_mgm_set = Arc::default();
|
||||||
let mgm_mode_leaf_interface = MpscModeLeafInterface {
|
let mgm_mode_leaf_interface = MpscModeLeafInterface {
|
||||||
@ -247,6 +249,7 @@ fn static_tmtc_pool_main() {
|
|||||||
mgm_mode_leaf_interface,
|
mgm_mode_leaf_interface,
|
||||||
mgm_handler_composite_rx,
|
mgm_handler_composite_rx,
|
||||||
pus_hk_reply_tx.clone(),
|
pus_hk_reply_tx.clone(),
|
||||||
|
switch_helper.clone(),
|
||||||
tm_sink_tx.clone(),
|
tm_sink_tx.clone(),
|
||||||
mgm_spi_interface,
|
mgm_spi_interface,
|
||||||
shared_mgm_set,
|
shared_mgm_set,
|
||||||
@ -507,6 +510,10 @@ fn dyn_tmtc_pool_main() {
|
|||||||
|
|
||||||
let mut tm_funnel = TmSinkDynamic::new(sync_tm_tcp_source, tm_funnel_rx, tm_server_tx);
|
let mut tm_funnel = TmSinkDynamic::new(sync_tm_tcp_source, tm_funnel_rx, tm_server_tx);
|
||||||
|
|
||||||
|
let shared_switch_set = Arc::new(Mutex::default());
|
||||||
|
let (switch_request_tx, switch_request_rx) = mpsc::sync_channel(20);
|
||||||
|
let switch_helper = PowerSwitchHelper::new(switch_request_tx, shared_switch_set.clone());
|
||||||
|
|
||||||
let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
|
let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
|
||||||
mpsc::channel();
|
mpsc::channel();
|
||||||
let shared_mgm_set = Arc::default();
|
let shared_mgm_set = Arc::default();
|
||||||
@ -531,6 +538,7 @@ fn dyn_tmtc_pool_main() {
|
|||||||
mode_leaf_interface,
|
mode_leaf_interface,
|
||||||
mgm_handler_composite_rx,
|
mgm_handler_composite_rx,
|
||||||
pus_hk_reply_tx,
|
pus_hk_reply_tx,
|
||||||
|
switch_helper.clone(),
|
||||||
tm_funnel_tx,
|
tm_funnel_tx,
|
||||||
mgm_spi_interface,
|
mgm_spi_interface,
|
||||||
shared_mgm_set,
|
shared_mgm_set,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -57,28 +59,28 @@ impl From<SwitchStateBinary> for SwitchState {
|
|||||||
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.
|
||||||
pub trait PowerSwitcherCommandSender {
|
pub trait PowerSwitcherCommandSender<SwitchType: Into<u16>> {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
fn send_switch_on_cmd(
|
fn send_switch_on_cmd(
|
||||||
&self,
|
&self,
|
||||||
requestor_info: MessageMetadata,
|
requestor_info: MessageMetadata,
|
||||||
switch_id: SwitchId,
|
switch_id: SwitchType,
|
||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), Self::Error>;
|
||||||
fn send_switch_off_cmd(
|
fn send_switch_off_cmd(
|
||||||
&self,
|
&self,
|
||||||
requestor_info: MessageMetadata,
|
requestor_info: MessageMetadata,
|
||||||
switch_id: SwitchId,
|
switch_id: SwitchType,
|
||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PowerSwitchInfo {
|
pub trait PowerSwitchInfo<SwitchType> {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
/// Retrieve the switch state
|
/// Retrieve the switch state
|
||||||
fn switch_state(&self, switch_id: SwitchId) -> Result<SwitchState, Self::Error>;
|
fn switch_state(&self, switch_id: SwitchType) -> Result<SwitchState, Self::Error>;
|
||||||
|
|
||||||
fn is_switch_on(&self, switch_id: SwitchId) -> Result<bool, Self::Error> {
|
fn is_switch_on(&self, switch_id: SwitchType) -> Result<bool, Self::Error> {
|
||||||
Ok(self.switch_state(switch_id)? == SwitchState::On)
|
Ok(self.switch_state(switch_id)? == SwitchState::On)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +88,7 @@ pub trait PowerSwitchInfo {
|
|||||||
///
|
///
|
||||||
/// This may take into account the time to send a command, wait for it to be executed, and
|
/// This may take into account the time to send a command, wait for it to be executed, and
|
||||||
/// see the switch changed.
|
/// see the switch changed.
|
||||||
fn switch_delay_ms(&self) -> u32;
|
fn switch_delay_ms(&self) -> Duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
@ -119,17 +121,17 @@ pub mod std_mod {
|
|||||||
pub type MpscSwitchCmdSender = mpsc::Sender<GenericMessage<SwitchRequest>>;
|
pub type MpscSwitchCmdSender = mpsc::Sender<GenericMessage<SwitchRequest>>;
|
||||||
pub type MpscSwitchCmdSenderBounded = mpsc::SyncSender<GenericMessage<SwitchRequest>>;
|
pub type MpscSwitchCmdSenderBounded = mpsc::SyncSender<GenericMessage<SwitchRequest>>;
|
||||||
|
|
||||||
impl PowerSwitcherCommandSender for MpscSwitchCmdSender {
|
impl<SwitchType: Into<u16>> PowerSwitcherCommandSender<SwitchType> for MpscSwitchCmdSender {
|
||||||
type Error = GenericSendError;
|
type Error = GenericSendError;
|
||||||
|
|
||||||
fn send_switch_on_cmd(
|
fn send_switch_on_cmd(
|
||||||
&self,
|
&self,
|
||||||
requestor_info: MessageMetadata,
|
requestor_info: MessageMetadata,
|
||||||
switch_id: SwitchId,
|
switch_id: SwitchType,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.send(GenericMessage::new(
|
self.send(GenericMessage::new(
|
||||||
requestor_info,
|
requestor_info,
|
||||||
SwitchRequest::new(switch_id, SwitchStateBinary::On),
|
SwitchRequest::new(switch_id.into(), SwitchStateBinary::On),
|
||||||
))
|
))
|
||||||
.map_err(|_| GenericSendError::RxDisconnected)
|
.map_err(|_| GenericSendError::RxDisconnected)
|
||||||
}
|
}
|
||||||
@ -137,27 +139,27 @@ pub mod std_mod {
|
|||||||
fn send_switch_off_cmd(
|
fn send_switch_off_cmd(
|
||||||
&self,
|
&self,
|
||||||
requestor_info: MessageMetadata,
|
requestor_info: MessageMetadata,
|
||||||
switch_id: SwitchId,
|
switch_id: SwitchType,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.send(GenericMessage::new(
|
self.send(GenericMessage::new(
|
||||||
requestor_info,
|
requestor_info,
|
||||||
SwitchRequest::new(switch_id, SwitchStateBinary::Off),
|
SwitchRequest::new(switch_id.into(), SwitchStateBinary::Off),
|
||||||
))
|
))
|
||||||
.map_err(|_| GenericSendError::RxDisconnected)
|
.map_err(|_| GenericSendError::RxDisconnected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PowerSwitcherCommandSender for MpscSwitchCmdSenderBounded {
|
impl<SwitchType: Into<u16>> PowerSwitcherCommandSender<SwitchType> for MpscSwitchCmdSenderBounded {
|
||||||
type Error = GenericSendError;
|
type Error = GenericSendError;
|
||||||
|
|
||||||
fn send_switch_on_cmd(
|
fn send_switch_on_cmd(
|
||||||
&self,
|
&self,
|
||||||
requestor_info: MessageMetadata,
|
requestor_info: MessageMetadata,
|
||||||
switch_id: SwitchId,
|
switch_id: SwitchType,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.try_send(GenericMessage::new(
|
self.try_send(GenericMessage::new(
|
||||||
requestor_info,
|
requestor_info,
|
||||||
SwitchRequest::new(switch_id, SwitchStateBinary::On),
|
SwitchRequest::new(switch_id.into(), SwitchStateBinary::On),
|
||||||
))
|
))
|
||||||
.map_err(|e| match e {
|
.map_err(|e| match e {
|
||||||
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
|
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
|
||||||
@ -168,11 +170,11 @@ pub mod std_mod {
|
|||||||
fn send_switch_off_cmd(
|
fn send_switch_off_cmd(
|
||||||
&self,
|
&self,
|
||||||
requestor_info: MessageMetadata,
|
requestor_info: MessageMetadata,
|
||||||
switch_id: SwitchId,
|
switch_id: SwitchType,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.try_send(GenericMessage::new(
|
self.try_send(GenericMessage::new(
|
||||||
requestor_info,
|
requestor_info,
|
||||||
SwitchRequest::new(switch_id, SwitchStateBinary::Off),
|
SwitchRequest::new(switch_id.into(), SwitchStateBinary::Off),
|
||||||
))
|
))
|
||||||
.map_err(|e| match e {
|
.map_err(|e| match e {
|
||||||
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
|
mpsc::TrySendError::Full(_) => GenericSendError::QueueFull(None),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user