extract switch and mode helper
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use satrs::fdir::FaultCounterStd;
|
||||
use satrs::health::{HealthState, HealthTableMapSync, HealthTableProvider};
|
||||
use satrs::spacepackets::CcsdsPacketIdAndPsc;
|
||||
use satrs_example::{HkHelperSingleSet, ModeHelper, TimestampHelper, TmtcQueues};
|
||||
use satrs_example::{HkHelperSingleSet, TimestampHelper, TmtcQueues};
|
||||
use satrs_minisim::acs::MgmRequestLis3Mdl;
|
||||
use satrs_minisim::acs::lis3mdl::{
|
||||
FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR, MgmLis3MdlReply, MgmLis3RawValues,
|
||||
@@ -17,6 +17,7 @@ use types::pcdu::SwitchId;
|
||||
use types::{ComponentId, DeviceMode, HkRequestType, acs::mgm};
|
||||
|
||||
use crate::ccsds::pack_ccsds_tm_packet_for_now;
|
||||
use crate::device_mode::{ModeTransitionEvent, SwitchAndModeHelper};
|
||||
use crate::eps::PowerSwitchHelper;
|
||||
|
||||
pub const NR_OF_DATA_AND_CFG_REGISTERS: usize = 14;
|
||||
@@ -56,14 +57,14 @@ impl MgmId {
|
||||
MgmId::_1 => ComponentId::AcsMgm1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub enum TransitionState {
|
||||
#[default]
|
||||
Idle,
|
||||
PowerSwitching,
|
||||
Done,
|
||||
#[inline]
|
||||
pub const fn switch_id(&self) -> SwitchId {
|
||||
match self {
|
||||
MgmId::_0 => SwitchId::Mgm0,
|
||||
MgmId::_1 => SwitchId::Mgm1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -159,14 +160,13 @@ pub struct ModeLeafHelper {
|
||||
/// Example MGM device handler strongly based on the LIS3MDL MEMS device.
|
||||
pub struct MgmHandlerLis3Mdl {
|
||||
id: MgmId,
|
||||
switch_helper: PowerSwitchHelper,
|
||||
tmtc_queues: TmtcQueues,
|
||||
pub spi_com: SpiCommunication,
|
||||
shared_mgm_set: Arc<Mutex<SensorData>>,
|
||||
buffers: BufWrapper,
|
||||
stamp_helper: TimestampHelper,
|
||||
hk_helper: HkHelperSingleSet,
|
||||
mode_helpers: ModeHelper<DeviceMode, TransitionState>,
|
||||
switch_and_mode_helper: SwitchAndModeHelper<DeviceMode>,
|
||||
mode_leaf_helper: ModeLeafHelper,
|
||||
spi_fault_counter: FaultCounterStd,
|
||||
health_table: HealthTableMapSync,
|
||||
@@ -187,10 +187,14 @@ impl MgmHandlerLis3Mdl {
|
||||
Self {
|
||||
id,
|
||||
tmtc_queues,
|
||||
switch_helper,
|
||||
spi_com,
|
||||
shared_mgm_set,
|
||||
mode_helpers: ModeHelper::new(DeviceMode::Off, mode_timeout),
|
||||
switch_and_mode_helper: SwitchAndModeHelper::new(
|
||||
DeviceMode::Off,
|
||||
mode_timeout,
|
||||
switch_helper,
|
||||
id.switch_id(),
|
||||
),
|
||||
buffers: BufWrapper::default(),
|
||||
stamp_helper: TimestampHelper::default(),
|
||||
hk_helper: HkHelperSingleSet::new(false, Duration::from_millis(200)),
|
||||
@@ -202,15 +206,7 @@ impl MgmHandlerLis3Mdl {
|
||||
|
||||
#[inline]
|
||||
pub fn mode(&self) -> DeviceMode {
|
||||
self.mode_helpers.current
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn switch_id(&self) -> SwitchId {
|
||||
match self.id {
|
||||
MgmId::_0 => SwitchId::Mgm0,
|
||||
MgmId::_1 => SwitchId::Mgm1,
|
||||
}
|
||||
self.switch_and_mode_helper.mode()
|
||||
}
|
||||
|
||||
pub fn periodic_operation(&mut self) {
|
||||
@@ -224,7 +220,16 @@ impl MgmHandlerLis3Mdl {
|
||||
self.handle_mode_leaf_handling();
|
||||
|
||||
// Handle mode transitions first.
|
||||
self.handle_mode_transition();
|
||||
if let Some(event) = self.switch_and_mode_helper.handle_mode_transition() {
|
||||
match event {
|
||||
ModeTransitionEvent::Reached(tc_commander) => {
|
||||
self.handle_mode_reached(tc_commander)
|
||||
}
|
||||
ModeTransitionEvent::Failed(tc_commander) => {
|
||||
self.handle_mode_transition_failure(tc_commander)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Poll sensor before checking and generating HK.
|
||||
if self.mode() == DeviceMode::Normal {
|
||||
@@ -259,8 +264,7 @@ impl MgmHandlerLis3Mdl {
|
||||
}
|
||||
mgm::request::Request::Mode(device_mode) => match device_mode {
|
||||
ModeRequest::SetMode(device_mode) => {
|
||||
self.mode_helpers.tc_commander = Some(tc_id);
|
||||
self.start_transition(device_mode, false);
|
||||
self.start_transition(device_mode, Some(tc_id));
|
||||
}
|
||||
ModeRequest::ReadMode => self.send_telemetry(
|
||||
Some(tc_id),
|
||||
@@ -309,7 +313,7 @@ impl MgmHandlerLis3Mdl {
|
||||
loop {
|
||||
match self.mode_leaf_helper.request_rx.try_recv() {
|
||||
Ok(request) => match request {
|
||||
ModeRequest::SetMode(device_mode) => self.start_transition(device_mode, false),
|
||||
ModeRequest::SetMode(device_mode) => self.start_transition(device_mode, None),
|
||||
ModeRequest::ReadMode => self.report_mode_to_parent(),
|
||||
},
|
||||
Err(e) => match e {
|
||||
@@ -445,59 +449,29 @@ impl MgmHandlerLis3Mdl {
|
||||
// this every cycle the fault persists, and current stays Normal until the
|
||||
// transition completes, so re-triggering here would keep resetting the
|
||||
// transition state machine before it can ever finish.
|
||||
if self.mode_helpers.target != Some(DeviceMode::Off) {
|
||||
if self.switch_and_mode_helper.target() != Some(DeviceMode::Off) {
|
||||
log::warn!("{}: commanding device off due to fault", self.id.str());
|
||||
self.start_transition(DeviceMode::Off, true);
|
||||
self.start_transition(DeviceMode::Off, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_transition(&mut self, target_mode: DeviceMode, _forced: bool) {
|
||||
fn start_transition(
|
||||
&mut self,
|
||||
target_mode: DeviceMode,
|
||||
tc_commander: Option<CcsdsPacketIdAndPsc>,
|
||||
) {
|
||||
log::info!("{}: transitioning to mode {:?}", self.id.str(), target_mode);
|
||||
if target_mode == DeviceMode::Off {
|
||||
self.shared_mgm_set.lock().unwrap().valid = false;
|
||||
}
|
||||
self.mode_helpers.start(target_mode);
|
||||
}
|
||||
|
||||
pub fn handle_mode_transition(&mut self) {
|
||||
if self.mode_helpers.target.is_none() {
|
||||
return;
|
||||
}
|
||||
let target_mode = self.mode_helpers.target.unwrap();
|
||||
let switch_target_on = target_mode != DeviceMode::Off;
|
||||
if self.mode_helpers.transition_state == TransitionState::Idle {
|
||||
let result = if switch_target_on {
|
||||
self.switch_helper.send_switch_on_cmd(self.switch_id())
|
||||
} else {
|
||||
self.switch_helper.send_switch_off_cmd(self.switch_id())
|
||||
};
|
||||
if result.is_err() {
|
||||
// Could not send switch command.. still continue with transition.
|
||||
log::error!(
|
||||
"failed to send switch {} command",
|
||||
if switch_target_on { "on" } else { "off" }
|
||||
);
|
||||
}
|
||||
self.mode_helpers.transition_state = TransitionState::PowerSwitching;
|
||||
}
|
||||
if self.mode_helpers.transition_state == TransitionState::PowerSwitching {
|
||||
if self.switch_helper.is_switch_on(self.switch_id()) == switch_target_on {
|
||||
log::info!("switch is {}", if switch_target_on { "on" } else { "off" });
|
||||
self.mode_helpers.transition_state = TransitionState::Done;
|
||||
} else if self.mode_helpers.timed_out() {
|
||||
self.handle_mode_transition_failure();
|
||||
}
|
||||
}
|
||||
if self.mode_helpers.transition_state == TransitionState::Done {
|
||||
self.handle_mode_reached();
|
||||
}
|
||||
self.switch_and_mode_helper
|
||||
.start_transition(target_mode, tc_commander);
|
||||
}
|
||||
|
||||
// Should be called to complete a mode transition which failed.
|
||||
fn handle_mode_transition_failure(&mut self) {
|
||||
let tc_commander = self.mode_helpers.finish(false);
|
||||
fn handle_mode_transition_failure(&mut self, tc_commander: Option<CcsdsPacketIdAndPsc>) {
|
||||
if tc_commander.is_some() {
|
||||
self.send_telemetry(
|
||||
tc_commander,
|
||||
@@ -511,8 +485,7 @@ impl MgmHandlerLis3Mdl {
|
||||
}
|
||||
|
||||
// Should be called to complete a mode transition successfully.
|
||||
fn handle_mode_reached(&mut self) {
|
||||
let tc_commander = self.mode_helpers.finish(true);
|
||||
fn handle_mode_reached(&mut self, tc_commander: Option<CcsdsPacketIdAndPsc>) {
|
||||
self.announce_mode();
|
||||
if let Some(requestor) = tc_commander {
|
||||
self.send_mode_tm(requestor);
|
||||
@@ -522,18 +495,14 @@ impl MgmHandlerLis3Mdl {
|
||||
}
|
||||
|
||||
fn announce_mode(&self) {
|
||||
log::info!(
|
||||
"{} announcing mode: {:?}",
|
||||
self.id.str(),
|
||||
self.mode_helpers.current
|
||||
);
|
||||
log::info!("{} announcing mode: {:?}", self.id.str(), self.mode());
|
||||
// TODO: Event?
|
||||
}
|
||||
|
||||
fn report_mode_to_parent(&self) {
|
||||
self.mode_leaf_helper
|
||||
.report_tx
|
||||
.send(ModeResponse::Mode(self.mode_helpers.current))
|
||||
.send(ModeResponse::Mode(self.mode()))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use types::pcdu::SwitchId;
|
||||
|
||||
use crate::eps::PowerSwitchHelper;
|
||||
|
||||
/// Modes that distinguish a powered-off state from one or more powered-on states, so
|
||||
/// [`SwitchAndModeHelper`] knows which way to drive the switch for a given target mode.
|
||||
pub trait PowerSwitchedMode: Copy + PartialEq {
|
||||
fn requires_power(&self) -> bool;
|
||||
}
|
||||
|
||||
impl PowerSwitchedMode for types::DeviceMode {
|
||||
fn requires_power(&self) -> bool {
|
||||
*self != types::DeviceMode::Off
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
enum SwitchTransitionState {
|
||||
#[default]
|
||||
Idle,
|
||||
PowerSwitching,
|
||||
Done,
|
||||
}
|
||||
|
||||
/// Outcome of a pending mode transition, once [`SwitchAndModeHelper::handle_mode_transition`]
|
||||
/// has driven it to completion. Carries back whichever TC commanded the transition, if any, so
|
||||
/// the caller can reply to it -- what that reply looks like is handler-specific, so this stays
|
||||
/// out of the helper.
|
||||
pub enum ModeTransitionEvent {
|
||||
Reached(Option<satrs::spacepackets::CcsdsPacketIdAndPsc>),
|
||||
Failed(Option<satrs::spacepackets::CcsdsPacketIdAndPsc>),
|
||||
}
|
||||
|
||||
/// Drives the on/off power-switch commanding state machine (Idle -> PowerSwitching -> Done)
|
||||
/// shared by every device handler that owns a single power switch of its own.
|
||||
///
|
||||
/// Handler-specific reactions (sending telemetry, invalidating cached sensor data, reporting to
|
||||
/// a parent) are not this helper's concern: [`Self::handle_mode_transition`] just reports when a
|
||||
/// transition finishes (or fails) and leaves what to do about it to the caller.
|
||||
pub struct SwitchAndModeHelper<Mode: PowerSwitchedMode> {
|
||||
mode_helper: satrs_example::ModeHelper<Mode, SwitchTransitionState>,
|
||||
switch_helper: PowerSwitchHelper,
|
||||
switch_id: SwitchId,
|
||||
}
|
||||
|
||||
impl<Mode: PowerSwitchedMode> SwitchAndModeHelper<Mode> {
|
||||
pub fn new(
|
||||
init_mode: Mode,
|
||||
timeout: Duration,
|
||||
switch_helper: PowerSwitchHelper,
|
||||
switch_id: SwitchId,
|
||||
) -> Self {
|
||||
Self {
|
||||
mode_helper: satrs_example::ModeHelper::new(init_mode, timeout),
|
||||
switch_helper,
|
||||
switch_id,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mode(&self) -> Mode {
|
||||
self.mode_helper.current
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn target(&self) -> Option<Mode> {
|
||||
self.mode_helper.target
|
||||
}
|
||||
|
||||
pub fn start_transition(
|
||||
&mut self,
|
||||
target_mode: Mode,
|
||||
tc_commander: Option<satrs::spacepackets::CcsdsPacketIdAndPsc>,
|
||||
) {
|
||||
self.mode_helper.tc_commander = tc_commander;
|
||||
self.mode_helper.start(target_mode);
|
||||
}
|
||||
|
||||
pub fn handle_mode_transition(&mut self) -> Option<ModeTransitionEvent> {
|
||||
let target_mode = self.mode_helper.target?;
|
||||
let switch_target_on = target_mode.requires_power();
|
||||
if self.mode_helper.transition_state == SwitchTransitionState::Idle {
|
||||
let result = if switch_target_on {
|
||||
self.switch_helper.send_switch_on_cmd(self.switch_id)
|
||||
} else {
|
||||
self.switch_helper.send_switch_off_cmd(self.switch_id)
|
||||
};
|
||||
if result.is_err() {
|
||||
// Could not send switch command.. still continue with transition.
|
||||
log::error!(
|
||||
"failed to send switch {} command",
|
||||
if switch_target_on { "on" } else { "off" }
|
||||
);
|
||||
}
|
||||
self.mode_helper.transition_state = SwitchTransitionState::PowerSwitching;
|
||||
}
|
||||
if self.mode_helper.transition_state == SwitchTransitionState::PowerSwitching {
|
||||
if self.switch_helper.is_switch_on(self.switch_id) == switch_target_on {
|
||||
log::info!("switch is {}", if switch_target_on { "on" } else { "off" });
|
||||
self.mode_helper.transition_state = SwitchTransitionState::Done;
|
||||
} else if self.mode_helper.timed_out() {
|
||||
return Some(ModeTransitionEvent::Failed(self.mode_helper.finish(false)));
|
||||
}
|
||||
}
|
||||
if self.mode_helper.transition_state == SwitchTransitionState::Done {
|
||||
return Some(ModeTransitionEvent::Reached(self.mode_helper.finish(true)));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -76,62 +76,6 @@ impl PowerSwitchHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
impl PowerSwitchInfo<SwitchId> for PowerSwitchHelper {
|
||||
type Error = SwitchInfoError;
|
||||
|
||||
fn switch_state(&self, switch_id: SwitchId) -> Result<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<SwitchId> for PowerSwitchHelper {
|
||||
type Error = SwitchCommandingError;
|
||||
|
||||
fn send_switch_on_cmd(
|
||||
&self,
|
||||
requestor_info: satrs::request::MessageMetadata,
|
||||
switch_id: SwitchId,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.switcher_tx.send(GenericMessage::new(
|
||||
requestor_info,
|
||||
SwitchRequest::new(switch_id, SwitchStateBinary::On),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_switch_off_cmd(
|
||||
&self,
|
||||
requestor_info: satrs::request::MessageMetadata,
|
||||
switch_id: SwitchId,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.switcher_tx.send(GenericMessage::new(
|
||||
requestor_info,
|
||||
SwitchRequest::new(switch_id, SwitchStateBinary::Off),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(new)]
|
||||
@@ -166,73 +110,6 @@ impl Default for TestSwitchHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl PowerSwitchInfo<SwitchId> for TestSwitchHelper {
|
||||
type Error = SwitchInfoError;
|
||||
|
||||
fn switch_state(&self, switch_id: SwitchId) -> 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<SwitchId> for TestSwitchHelper {
|
||||
type Error = SwitchCommandingError;
|
||||
|
||||
fn send_switch_on_cmd(
|
||||
&self,
|
||||
requestor_info: MessageMetadata,
|
||||
switch_id: SwitchId,
|
||||
) -> 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: SwitchId,
|
||||
) -> 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(())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl TestSwitchHelper {
|
||||
// Helper function which can be used to force a switch to another state for test purposes.
|
||||
|
||||
@@ -48,6 +48,7 @@ use crate::{
|
||||
mod acs;
|
||||
mod ccsds;
|
||||
mod controller;
|
||||
mod device_mode;
|
||||
mod eps;
|
||||
mod event_manager;
|
||||
mod interface;
|
||||
|
||||
Reference in New Issue
Block a user