244 lines
8.1 KiB
Rust
244 lines
8.1 KiB
Rust
use std::{
|
|
sync::mpsc::{self, Receiver, SyncSender},
|
|
time::Duration,
|
|
};
|
|
|
|
use models::{
|
|
ComponentId,
|
|
acs::subsystem::{Mode, response},
|
|
};
|
|
use satrs::spacepackets::CcsdsPacketIdAndPsc;
|
|
use satrs_example::{ModeHelper, TmtcQueues};
|
|
|
|
#[derive(Debug)]
|
|
pub struct TransitionInfo {
|
|
check_mode_reached: bool,
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
pub enum TransitionState {
|
|
#[default]
|
|
Idle,
|
|
AwaitingReplies,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ChildModes {
|
|
mgm_assembly_mode: models::acs::mgm_assembly::Mode,
|
|
mgt_mode: models::acs::mgt::Mode,
|
|
controller_mode: models::acs::ctrl::Mode,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TransitionCommands {
|
|
mgm_assembly_mode: Option<(models::acs::mgm_assembly::Mode, TransitionInfo)>,
|
|
mgt_mode: Option<(models::acs::mgt::Mode, TransitionInfo)>,
|
|
controller_mode: Option<(models::acs::ctrl::Mode, TransitionInfo)>,
|
|
}
|
|
|
|
const OFF_SEQUENCE: [TransitionCommands; 2] = [
|
|
TransitionCommands {
|
|
mgm_assembly_mode: None,
|
|
mgt_mode: None,
|
|
controller_mode: Some((
|
|
models::acs::ctrl::Mode::Passive,
|
|
TransitionInfo {
|
|
check_mode_reached: true,
|
|
},
|
|
)),
|
|
},
|
|
TransitionCommands {
|
|
mgm_assembly_mode: Some((
|
|
models::acs::mgm_assembly::Mode::Device(models::DeviceMode::Off),
|
|
TransitionInfo {
|
|
check_mode_reached: false,
|
|
},
|
|
)),
|
|
mgt_mode: Some((
|
|
models::acs::mgt::Mode::Off,
|
|
TransitionInfo {
|
|
check_mode_reached: false,
|
|
},
|
|
)),
|
|
controller_mode: None,
|
|
},
|
|
];
|
|
|
|
const SAFE_SEQUENCE: [TransitionCommands; 2] = [
|
|
TransitionCommands {
|
|
mgm_assembly_mode: Some((
|
|
models::acs::mgm_assembly::Mode::Device(models::DeviceMode::Normal),
|
|
TransitionInfo {
|
|
check_mode_reached: false,
|
|
},
|
|
)),
|
|
mgt_mode: Some((
|
|
models::acs::mgt::Mode::Normal,
|
|
TransitionInfo {
|
|
check_mode_reached: false,
|
|
},
|
|
)),
|
|
controller_mode: None,
|
|
},
|
|
TransitionCommands {
|
|
mgm_assembly_mode: None,
|
|
mgt_mode: None,
|
|
controller_mode: Some((
|
|
models::acs::ctrl::Mode::Safe,
|
|
TransitionInfo {
|
|
check_mode_reached: false,
|
|
},
|
|
)),
|
|
},
|
|
];
|
|
|
|
#[derive(Debug)]
|
|
pub struct ModeRequestSenders {
|
|
pub mode_request_ctrl: SyncSender<models::acs::ctrl::request::ModeRequest>,
|
|
pub mode_request_assy: SyncSender<models::acs::mgm_assembly::request::ModeRequest>,
|
|
pub mode_request_mgt: SyncSender<models::acs::mgt::request::ModeRequest>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ModeReportReceivers {
|
|
pub mode_response_ctrl: Receiver<models::acs::ctrl::response::ModeReport>,
|
|
pub mode_response_assy: Receiver<models::acs::mgm_assembly::response::ModeResponse>,
|
|
pub mode_response_mgt: Receiver<models::acs::mgt::response::ModeReport>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Subsystem {
|
|
mode_helper: ModeHelper<models::acs::subsystem::Mode, TransitionState>,
|
|
transition_step: usize,
|
|
current_child_modes: Option<ChildModes>,
|
|
mode_request_senders: ModeRequestSenders,
|
|
mode_report_receivers: ModeReportReceivers,
|
|
tmtc_queues: TmtcQueues,
|
|
}
|
|
|
|
impl Subsystem {
|
|
pub const ID: ComponentId = ComponentId::AcsSubsystem;
|
|
|
|
pub fn new(
|
|
mode_request_senders: ModeRequestSenders,
|
|
mode_report_receivers: ModeReportReceivers,
|
|
tmtc_queues: TmtcQueues,
|
|
) -> Self {
|
|
Self {
|
|
mode_helper: ModeHelper::new(
|
|
models::acs::subsystem::Mode::Off,
|
|
Duration::from_millis(2000),
|
|
),
|
|
current_child_modes: None,
|
|
mode_request_senders,
|
|
mode_report_receivers,
|
|
tmtc_queues,
|
|
transition_step: 0,
|
|
}
|
|
}
|
|
|
|
pub fn periodic_operation(&mut self) {
|
|
self.handle_telecommands();
|
|
}
|
|
|
|
pub fn handle_telecommands(&mut self) {
|
|
loop {
|
|
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::<models::acs::subsystem::request::Request>(
|
|
&packet.payload,
|
|
) {
|
|
Ok(request) => match request {
|
|
models::acs::subsystem::request::Request::Ping => {
|
|
self.send_telemetry(Some(tc_id), response::Response::Ok)
|
|
}
|
|
models::acs::subsystem::request::Request::Mode(mode_request) => {
|
|
self.handle_mode_request(mode_request);
|
|
}
|
|
},
|
|
Err(e) => {
|
|
log::warn!("failed to deserialize request: {}", e);
|
|
}
|
|
}
|
|
}
|
|
Err(e) => match e {
|
|
mpsc::TryRecvError::Empty => break,
|
|
mpsc::TryRecvError::Disconnected => log::warn!("packet sender disconnected"),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn transition_sequence_for_mode(mode: Mode) -> &'static [TransitionCommands] {
|
|
match mode {
|
|
Mode::Off => &OFF_SEQUENCE,
|
|
Mode::Safe => &SAFE_SEQUENCE,
|
|
}
|
|
}
|
|
|
|
pub fn handle_mode_request(
|
|
&mut self,
|
|
mode_request: models::acs::subsystem::request::ModeRequest,
|
|
) {
|
|
match mode_request {
|
|
models::acs::subsystem::request::ModeRequest::SetMode(target_mode) => {
|
|
self.mode_helper.start(target_mode);
|
|
match target_mode {
|
|
models::acs::subsystem::Mode::Off => {
|
|
// TODO: We need to start executing the transition table here..
|
|
/*
|
|
self.mode_request_senders
|
|
.mode_request_assy
|
|
.send(models::acs::mgm_assembly::request::ModeRequest::SetMode(
|
|
models::acs::mgm_assembly::Mode::Device(models::DeviceMode::Off),
|
|
))
|
|
.expect("failed to send mode request to MGM assembly");
|
|
self.mode_request_senders
|
|
.mode_request_mgt
|
|
.send(models::acs::mgt::request::ModeRequest::SetMode(
|
|
models::acs::mgt::Mode::Normal,
|
|
))
|
|
.expect("failed to send mode request to MGM assembly");
|
|
self.mode_request_senders
|
|
.mode_request_ctrl
|
|
.send(models::acs::ctrl::request::ModeRequest::SetMode(
|
|
models::acs::ctrl::Mode::Passive,
|
|
))
|
|
.expect("failed to send mode request to MGM assembly");
|
|
*/
|
|
}
|
|
models::acs::subsystem::Mode::Safe => {
|
|
// TODO: Perform SAFE sequence.
|
|
}
|
|
}
|
|
}
|
|
models::acs::subsystem::request::ModeRequest::ReadMode => {
|
|
self.send_telemetry(
|
|
None,
|
|
response::Response::Mode(response::ModeResponse::Mode(
|
|
self.mode_helper.current,
|
|
)),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn send_telemetry(
|
|
&self,
|
|
tc_id: Option<CcsdsPacketIdAndPsc>,
|
|
response: models::acs::subsystem::response::Response,
|
|
) {
|
|
match crate::ccsds::pack_ccsds_tm_packet_for_now(Self::ID, tc_id, &response) {
|
|
Ok(packet) => {
|
|
if let Err(e) = self.tmtc_queues.tm_tx.send(packet) {
|
|
log::warn!("failed to send TM packet: {}", e);
|
|
}
|
|
}
|
|
Err(e) => {
|
|
log::warn!("failed to pack TM packet: {}", e);
|
|
}
|
|
}
|
|
}
|
|
}
|