introduce mode commands to python client
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
575003db94
commit
68908f53d4
@ -22,6 +22,25 @@ def create_cmd_definition_tree() -> CmdTreeNode:
|
|||||||
|
|
||||||
root_node = CmdTreeNode.root_node()
|
root_node = CmdTreeNode.root_node()
|
||||||
|
|
||||||
|
hk_node = CmdTreeNode("hk", "Housekeeping Node", hide_children_for_print=True)
|
||||||
|
hk_node.add_child(CmdTreeNode("one_shot_hk", "Request One Shot HK set"))
|
||||||
|
hk_node.add_child(
|
||||||
|
CmdTreeNode("enable", "Enable periodic housekeeping data generation")
|
||||||
|
)
|
||||||
|
hk_node.add_child(
|
||||||
|
CmdTreeNode("disable", "Disable periodic housekeeping data generation")
|
||||||
|
)
|
||||||
|
|
||||||
|
mode_node = CmdTreeNode("mode", "Mode Node", hide_children_for_print=True)
|
||||||
|
set_mode_node = CmdTreeNode(
|
||||||
|
"set_mode", "Set Node", hide_children_which_are_leaves=True
|
||||||
|
)
|
||||||
|
set_mode_node.add_child(CmdTreeNode("off", "Set OFF Mode"))
|
||||||
|
set_mode_node.add_child(CmdTreeNode("on", "Set ON Mode"))
|
||||||
|
set_mode_node.add_child(CmdTreeNode("normal", "Set NORMAL Mode"))
|
||||||
|
mode_node.add_child(set_mode_node)
|
||||||
|
mode_node.add_child(CmdTreeNode("read_mode", "Read Mode"))
|
||||||
|
|
||||||
test_node = CmdTreeNode("test", "Test Node")
|
test_node = CmdTreeNode("test", "Test Node")
|
||||||
test_node.add_child(CmdTreeNode("ping", "Send PUS ping TC"))
|
test_node.add_child(CmdTreeNode("ping", "Send PUS ping TC"))
|
||||||
test_node.add_child(CmdTreeNode("trigger_event", "Send PUS test to trigger event"))
|
test_node.add_child(CmdTreeNode("trigger_event", "Send PUS test to trigger event"))
|
||||||
@ -37,7 +56,9 @@ def create_cmd_definition_tree() -> CmdTreeNode:
|
|||||||
|
|
||||||
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
|
acs_node = CmdTreeNode("acs", "ACS Subsystem Node")
|
||||||
mgm_node = CmdTreeNode("mgms", "MGM devices node")
|
mgm_node = CmdTreeNode("mgms", "MGM devices node")
|
||||||
mgm_node.add_child(CmdTreeNode("one_shot_hk", "Request one shot HK"))
|
mgm_node.add_child(mode_node)
|
||||||
|
mgm_node.add_child(hk_node)
|
||||||
|
|
||||||
acs_node.add_child(mgm_node)
|
acs_node.add_child(mgm_node)
|
||||||
root_node.add_child(acs_node)
|
root_node.add_child(acs_node)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ 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};
|
||||||
use satrs::spacepackets::SpHeader;
|
use satrs::spacepackets::SpHeader;
|
||||||
use satrs_example::TimeStampHelper;
|
use satrs_example::{DeviceMode, TimeStampHelper};
|
||||||
use std::sync::mpsc::{self};
|
use std::sync::mpsc::{self};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@ -20,6 +20,10 @@ use crate::requests::CompositeRequest;
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
const GAUSS_TO_MICROTESLA_FACTOR: f32 = 100.0;
|
||||||
|
// This is the selected resoltion for the STM LIS3MDL device for the 4 Gauss sensitivity setting.
|
||||||
|
const FIELD_LSB_PER_GAUSS_4_SENS: f32 = 1.0 / 6842.0;
|
||||||
|
|
||||||
pub trait SpiInterface {
|
pub trait SpiInterface {
|
||||||
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>;
|
||||||
@ -27,9 +31,9 @@ pub trait SpiInterface {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SpiDummyInterface {
|
pub struct SpiDummyInterface {
|
||||||
dummy_val_0: i16,
|
pub dummy_val_0: i16,
|
||||||
dummy_val_1: i16,
|
pub dummy_val_1: i16,
|
||||||
dummy_val_2: i16,
|
pub dummy_val_2: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpiInterface for SpiDummyInterface {
|
impl SpiInterface for SpiDummyInterface {
|
||||||
@ -45,6 +49,7 @@ impl SpiInterface for SpiDummyInterface {
|
|||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
|
||||||
pub struct MgmData {
|
pub struct MgmData {
|
||||||
|
pub valid: bool,
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
pub z: f32,
|
pub z: f32,
|
||||||
@ -56,9 +61,10 @@ pub struct MpscModeLeafInterface {
|
|||||||
pub reply_tx_to_parent: mpsc::Sender<GenericMessage<ModeReply>>,
|
pub reply_tx_to_parent: mpsc::Sender<GenericMessage<ModeReply>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 MgmHandler<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> {
|
pub struct MgmHandlerLis3Mdl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> {
|
||||||
id: UniqueApidTargetId,
|
id: UniqueApidTargetId,
|
||||||
dev_str: &'static str,
|
dev_str: &'static str,
|
||||||
mode_interface: MpscModeLeafInterface,
|
mode_interface: MpscModeLeafInterface,
|
||||||
@ -68,26 +74,41 @@ pub struct MgmHandler<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> {
|
|||||||
com_interface: ComInterface,
|
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: ModeAndSubmode,
|
mode_and_submode: ModeAndSubmode,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
tx_buf: [u8; 12],
|
tx_buf: [u8; 12],
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
rx_buf: [u8; 12],
|
rx_buf: [u8; 12],
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
tm_buf: [u8; 12],
|
tm_buf: [u8; 16],
|
||||||
|
#[new(default)]
|
||||||
stamp_helper: TimeStampHelper,
|
stamp_helper: TimeStampHelper,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> MgmHandler<ComInterface, TmSender> {
|
impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore>
|
||||||
|
MgmHandlerLis3Mdl<ComInterface, TmSender>
|
||||||
|
{
|
||||||
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.
|
||||||
self.handle_composite_requests();
|
self.handle_composite_requests();
|
||||||
self.handle_mode_requests();
|
self.handle_mode_requests();
|
||||||
// Communicate with the device.
|
if self.mode() == DeviceMode::Normal as u32 {
|
||||||
let result = self.com_interface.transfer(&self.tx_buf, &mut self.rx_buf);
|
log::trace!("polling LIS3MDL sensor {}", self.dev_str);
|
||||||
assert!(result.is_ok());
|
// Communicate with the device.
|
||||||
// TODO: Convert the raw data back to floats.
|
let result = self.com_interface.transfer(&self.tx_buf, &mut self.rx_buf);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
// Actual data begins on the second byte, similarly to how a lot of SPI devices behave.
|
||||||
|
let x_raw = i16::from_be_bytes(self.rx_buf[1..3].try_into().unwrap());
|
||||||
|
let y_raw = i16::from_be_bytes(self.rx_buf[3..5].try_into().unwrap());
|
||||||
|
let z_raw = i16::from_be_bytes(self.rx_buf[5..7].try_into().unwrap());
|
||||||
|
// Simple scaling to retrieve the float value, assuming a sensor resolution of
|
||||||
|
let mut mgm_guard = self.shared_mgm_set.lock().unwrap();
|
||||||
|
mgm_guard.x = x_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS;
|
||||||
|
mgm_guard.y = y_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS;
|
||||||
|
mgm_guard.z = z_raw as f32 * GAUSS_TO_MICROTESLA_FACTOR * FIELD_LSB_PER_GAUSS_4_SENS;
|
||||||
|
drop(mgm_guard);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_composite_requests(&mut self) {
|
pub fn handle_composite_requests(&mut self) {
|
||||||
@ -134,13 +155,14 @@ impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> MgmHandler<ComInter
|
|||||||
0,
|
0,
|
||||||
Some(self.stamp_helper.stamp()),
|
Some(self.stamp_helper.stamp()),
|
||||||
);
|
);
|
||||||
// Let's serialize it as JSON for now.. This is a lot simpler than binary
|
|
||||||
// serialization.
|
|
||||||
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(&mgm_snapshot.x.to_be_bytes());
|
// Use binary serialization here. We want the data to be tightly packed.
|
||||||
self.tm_buf[4..8].copy_from_slice(&mgm_snapshot.y.to_be_bytes());
|
self.tm_buf[0] = mgm_snapshot.valid as u8;
|
||||||
self.tm_buf[8..12].copy_from_slice(&mgm_snapshot.z.to_be_bytes());
|
self.tm_buf[1..5].copy_from_slice(&mgm_snapshot.x.to_be_bytes());
|
||||||
let hk_tm = PusTmCreator::new(&mut sp_header, sec_header, &self.tm_buf[0..12], true);
|
self.tm_buf[5..9].copy_from_slice(&mgm_snapshot.y.to_be_bytes());
|
||||||
|
self.tm_buf[9..13].copy_from_slice(&mgm_snapshot.z.to_be_bytes());
|
||||||
|
let hk_tm =
|
||||||
|
PusTmCreator::new(&mut sp_header, sec_header, &self.tm_buf[0..12], true);
|
||||||
self.tm_sender
|
self.tm_sender
|
||||||
.send_tm(self.id.id(), PusTmVariant::Direct(hk_tm))
|
.send_tm(self.id.id(), PusTmVariant::Direct(hk_tm))
|
||||||
.expect("failed to send HK TM");
|
.expect("failed to send HK TM");
|
||||||
@ -179,15 +201,15 @@ impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> MgmHandler<ComInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> ModeProvider
|
impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> ModeProvider
|
||||||
for MgmHandler<ComInterface, TmSender>
|
for MgmHandlerLis3Mdl<ComInterface, TmSender>
|
||||||
{
|
{
|
||||||
fn mode_and_submode(&self) -> ModeAndSubmode {
|
fn mode_and_submode(&self) -> ModeAndSubmode {
|
||||||
self.mode
|
self.mode_and_submode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> ModeRequestHandler
|
impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> ModeRequestHandler
|
||||||
for MgmHandler<ComInterface, TmSender>
|
for MgmHandlerLis3Mdl<ComInterface, TmSender>
|
||||||
{
|
{
|
||||||
type Error = ModeError;
|
type Error = ModeError;
|
||||||
fn start_transition(
|
fn start_transition(
|
||||||
@ -195,14 +217,22 @@ impl<ComInterface: SpiInterface, TmSender: EcssTmSenderCore> ModeRequestHandler
|
|||||||
requestor: MessageMetadata,
|
requestor: MessageMetadata,
|
||||||
mode_and_submode: ModeAndSubmode,
|
mode_and_submode: ModeAndSubmode,
|
||||||
) -> Result<(), satrs::mode::ModeError> {
|
) -> Result<(), satrs::mode::ModeError> {
|
||||||
self.mode = mode_and_submode;
|
log::info!(
|
||||||
|
"{}: transitioning to mode {:?}",
|
||||||
|
self.dev_str,
|
||||||
|
mode_and_submode
|
||||||
|
);
|
||||||
|
self.mode_and_submode = mode_and_submode;
|
||||||
self.handle_mode_reached(Some(requestor))?;
|
self.handle_mode_reached(Some(requestor))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn announce_mode(&self, _requestor_info: Option<MessageMetadata>, _recursive: bool) {
|
fn announce_mode(&self, _requestor_info: Option<MessageMetadata>, _recursive: bool) {
|
||||||
log::info!("{} announcing mode: {:?}", self.dev_str, self.mode);
|
log::info!(
|
||||||
// TODO: Trigger event.
|
"{} announcing mode: {:?}",
|
||||||
|
self.dev_str,
|
||||||
|
self.mode_and_submode
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_mode_reached(
|
fn handle_mode_reached(
|
||||||
|
@ -27,7 +27,7 @@ use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
|
|||||||
use tmtc::PusTcSourceProviderDynamic;
|
use tmtc::PusTcSourceProviderDynamic;
|
||||||
use udp::DynamicUdpTmHandler;
|
use udp::DynamicUdpTmHandler;
|
||||||
|
|
||||||
use crate::acs::mgm::{MgmHandler, MpscModeLeafInterface, SpiDummyInterface};
|
use crate::acs::mgm::{MgmHandlerLis3Mdl, MpscModeLeafInterface, SpiDummyInterface};
|
||||||
use crate::ccsds::CcsdsReceiver;
|
use crate::ccsds::CcsdsReceiver;
|
||||||
use crate::logger::setup_logger;
|
use crate::logger::setup_logger;
|
||||||
use crate::pus::action::{create_action_service_dynamic, create_action_service_static};
|
use crate::pus::action::{create_action_service_dynamic, create_action_service_static};
|
||||||
@ -211,7 +211,7 @@ fn static_tmtc_pool_main() {
|
|||||||
reply_tx_to_pus: pus_mode_reply_tx,
|
reply_tx_to_pus: pus_mode_reply_tx,
|
||||||
reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx,
|
reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx,
|
||||||
};
|
};
|
||||||
let mut mgm_handler = MgmHandler::new(
|
let mut mgm_handler = MgmHandlerLis3Mdl::new(
|
||||||
MGM_HANDLER_0,
|
MGM_HANDLER_0,
|
||||||
"MGM_0",
|
"MGM_0",
|
||||||
mode_leaf_interface,
|
mode_leaf_interface,
|
||||||
@ -429,7 +429,7 @@ fn dyn_tmtc_pool_main() {
|
|||||||
reply_tx_to_pus: pus_mode_reply_tx,
|
reply_tx_to_pus: pus_mode_reply_tx,
|
||||||
reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx,
|
reply_tx_to_parent: mgm_handler_mode_reply_to_parent_tx,
|
||||||
};
|
};
|
||||||
let mut mgm_handler = MgmHandler::new(
|
let mut mgm_handler = MgmHandlerLis3Mdl::new(
|
||||||
MGM_HANDLER_0,
|
MGM_HANDLER_0,
|
||||||
"MGM_0",
|
"MGM_0",
|
||||||
mode_leaf_interface,
|
mode_leaf_interface,
|
||||||
|
@ -179,6 +179,14 @@ impl From<GenericTargetedMessagingError> for ModeError {
|
|||||||
|
|
||||||
pub trait ModeProvider {
|
pub trait ModeProvider {
|
||||||
fn mode_and_submode(&self) -> ModeAndSubmode;
|
fn mode_and_submode(&self) -> ModeAndSubmode;
|
||||||
|
|
||||||
|
fn mode(&self) -> Mode {
|
||||||
|
self.mode_and_submode().mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submode(&self) -> Submode {
|
||||||
|
self.mode_and_submode().submode()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ModeRequestHandler: ModeProvider {
|
pub trait ModeRequestHandler: ModeProvider {
|
||||||
|
Loading…
Reference in New Issue
Block a user