use edition 2024, continue re-work
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "satrs-example"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
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"
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<SimRequest>,
|
||||
pub sim_reply_rx: mpsc::Receiver<SimReply>,
|
||||
@@ -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<CcsdsTcPacketOwned>,
|
||||
tm_tx: mpsc::SyncSender<CcsdsTmPacketOwned>,
|
||||
switch_helper: PowerSwitchHelper,
|
||||
pub com_interface: SpiCommunication,
|
||||
tmtc_queues: TmtcQueues,
|
||||
pub spi_com: SpiCommunication,
|
||||
shared_mgm_set: Arc<Mutex<MgmData>>,
|
||||
buffers: BufWrapper,
|
||||
stamp_helper: TimestampHelper,
|
||||
hk_helper: HkHelperSingleSet,
|
||||
mode_helpers: ModeHelper<DeviceMode>,
|
||||
mode_helpers: ModeHelper<DeviceMode, TransitionState>,
|
||||
mode_leaf_helper: ModeLeafHelper,
|
||||
}
|
||||
|
||||
impl MgmHandlerLis3Mdl {
|
||||
pub fn new(
|
||||
id: MgmId,
|
||||
tc_rx: mpsc::Receiver<CcsdsTcPacketOwned>,
|
||||
tm_tx: mpsc::SyncSender<CcsdsTmPacketOwned>,
|
||||
tmtc_queues: TmtcQueues,
|
||||
switch_helper: PowerSwitchHelper,
|
||||
com_interface: SpiCommunication,
|
||||
spi_com: SpiCommunication,
|
||||
shared_mgm_set: Arc<Mutex<MgmData>>,
|
||||
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::<mgm::request::Request>(&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<ModeRequest>,
|
||||
@@ -501,7 +501,7 @@ mod tests {
|
||||
pub tc_tx: mpsc::SyncSender<CcsdsTcPacketOwned>,
|
||||
pub tm_rx: mpsc::Receiver<CcsdsTmPacketOwned>,
|
||||
pub switch_rx: mpsc::Receiver<GenericMessage<SwitchRequest>>,
|
||||
pub handler: MgmHandlerLis3Mdl<TestSpiInterface>,
|
||||
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(
|
||||
|
||||
@@ -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<super::mgm::ModeReport>; 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<DeviceMode>,
|
||||
mode_helper: ModeHelper<AssemblyMode, TransitionState>,
|
||||
/// 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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use models::{
|
||||
ComponentId,
|
||||
ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned},
|
||||
control, ComponentId,
|
||||
control,
|
||||
};
|
||||
use satrs::spacepackets::CcsdsPacketIdAndPsc;
|
||||
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+18
-11
@@ -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<CcsdsTcPacketOwned>,
|
||||
pub tm_tx: mpsc::SyncSender<CcsdsTmPacketOwned>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ModeHelper<Mode> {
|
||||
pub struct ModeHelper<Mode, TransitionState> {
|
||||
pub current: Mode,
|
||||
pub target: Option<Mode>,
|
||||
pub tc_id: Option<CcsdsPacketIdAndPsc>,
|
||||
@@ -109,7 +110,7 @@ pub struct ModeHelper<Mode> {
|
||||
pub transition_state: TransitionState,
|
||||
}
|
||||
|
||||
impl<Mode: Copy + Clone> ModeHelper<Mode> {
|
||||
impl<Mode: Copy + Clone, TransitionState: Default> ModeHelper<Mode, TransitionState> {
|
||||
pub fn new(init_mode: Mode, timeout: Duration) -> Self {
|
||||
Self {
|
||||
current: init_mode,
|
||||
@@ -124,7 +125,12 @@ impl<Mode: Copy + Clone> ModeHelper<Mode> {
|
||||
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<Mode: Copy + Clone> ModeHelper<Mode> {
|
||||
} else {
|
||||
self.target = None;
|
||||
}
|
||||
self.transition_state = Default::default();
|
||||
self.transition_start = None;
|
||||
}
|
||||
}
|
||||
|
||||
+62
-40
@@ -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();
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use models::{ccsds::CcsdsTcPacketOwned, ComponentId, TcHeader};
|
||||
use models::{ComponentId, TcHeader, ccsds::CcsdsTcPacketOwned};
|
||||
use satrs::{
|
||||
pus::HandlingStatus,
|
||||
spacepackets::{CcsdsPacketReader, ChecksumType},
|
||||
|
||||
Reference in New Issue
Block a user