got an assembly helper now

This commit is contained in:
Robin Müller 2025-01-17 14:18:16 +01:00
parent 42ad2ce9f5
commit 1263a3a3dd
Signed by: muellerr
GPG Key ID: A649FB78196E3849
4 changed files with 217 additions and 122 deletions

View File

@ -180,6 +180,8 @@ impl<R: MessageReceiverProvider<ModeRequest>> ModeRequestReceiver
pub enum ModeError { pub enum ModeError {
#[error("Messaging error: {0}")] #[error("Messaging error: {0}")]
Messaging(#[from] GenericTargetedMessagingError), Messaging(#[from] GenericTargetedMessagingError),
#[error("busy with other mode request")]
Busy,
} }
pub trait ModeProvider { pub trait ModeProvider {

View File

@ -282,7 +282,7 @@ pub mod alloc_mod {
#[derive(Debug)] #[derive(Debug)]
pub struct ModeStoreVecValue { pub struct ModeStoreVecValue {
id: ComponentId, id: ComponentId,
mode_and_submode: ModeAndSubmode, pub mode_and_submode: ModeAndSubmode,
pub awaiting_reply: bool, pub awaiting_reply: bool,
} }
@ -309,6 +309,30 @@ pub mod alloc_mod {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ModeStoreMap(pub hashbrown::HashMap<ComponentId, ModeAndSubmode>); pub struct ModeStoreMap(pub hashbrown::HashMap<ComponentId, ModeAndSubmode>);
impl ModeStoreVec {
/// Generic handler for mode replies received from child components.
///
/// It returns whether any children are still awating replies.
pub fn generic_reply_handler(
&mut self,
sender_id: ComponentId,
reported_mode_and_submode: Option<ModeAndSubmode>,
) -> bool {
let mut still_awating_replies = false;
self.0.iter_mut().for_each(|val| {
if val.id() == sender_id {
if let Some(mode_and_submode) = reported_mode_and_submode {
val.mode_and_submode = mode_and_submode;
}
val.awaiting_reply = false;
}
if val.awaiting_reply {
still_awating_replies = true;
}
});
still_awating_replies
}
}
impl ModeStoreProvider for ModeStoreVec { impl ModeStoreProvider for ModeStoreVec {
fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) { fn add_component(&mut self, target_id: ComponentId, mode: ModeAndSubmode) {
self.0.push(ModeStoreVecValue::new(target_id, mode)); self.0.push(ModeStoreVecValue::new(target_id, mode));

View File

@ -28,9 +28,9 @@ pub enum TargetKeepingResult {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum SequenceHandlerResult { pub enum ModeCommandingResult {
SequenceDone, CommandingDone,
SequenceStepDone, CommandingStepDone,
AwaitingSuccessCheck, AwaitingSuccessCheck,
} }
@ -103,12 +103,12 @@ impl SequenceExecutionHelper {
table: &SequenceModeTables, table: &SequenceModeTables,
sender: &impl ModeRequestSender, sender: &impl ModeRequestSender,
mode_store_vec: &mut ModeStoreVec, mode_store_vec: &mut ModeStoreVec,
) -> Result<SequenceHandlerResult, GenericTargetedMessagingError> { ) -> Result<ModeCommandingResult, GenericTargetedMessagingError> {
if self.state == SequenceExecutionHelperStates::AwaitingCheckSuccess { if self.state == SequenceExecutionHelperStates::AwaitingCheckSuccess {
return Ok(SequenceHandlerResult::AwaitingSuccessCheck); return Ok(ModeCommandingResult::AwaitingSuccessCheck);
} }
if self.target_mode.is_none() { if self.target_mode.is_none() {
return Ok(SequenceHandlerResult::SequenceDone); return Ok(ModeCommandingResult::CommandingDone);
} }
match self.current_sequence_index { match self.current_sequence_index {
Some(idx) => { Some(idx) => {
@ -125,7 +125,7 @@ impl SequenceExecutionHelper {
// Find the first sequence // Find the first sequence
let seq_table_value = table.0.get(&self.target_mode.unwrap()).unwrap(); let seq_table_value = table.0.get(&self.target_mode.unwrap()).unwrap();
if seq_table_value.entries.is_empty() { if seq_table_value.entries.is_empty() {
Ok(SequenceHandlerResult::SequenceDone) Ok(ModeCommandingResult::CommandingDone)
} else { } else {
self.current_sequence_index = Some(0); self.current_sequence_index = Some(0);
self.execute_sequence_and_map_to_result( self.execute_sequence_and_map_to_result(
@ -145,7 +145,7 @@ impl SequenceExecutionHelper {
sequence_idx: usize, sequence_idx: usize,
sender: &impl ModeRequestSender, sender: &impl ModeRequestSender,
mode_store_vec: &mut ModeStoreVec, mode_store_vec: &mut ModeStoreVec,
) -> Result<SequenceHandlerResult, GenericTargetedMessagingError> { ) -> Result<ModeCommandingResult, GenericTargetedMessagingError> {
if Self::execute_sequence( if Self::execute_sequence(
self.request_id, self.request_id,
&seq_table_value.entries[sequence_idx], &seq_table_value.entries[sequence_idx],
@ -153,12 +153,12 @@ impl SequenceExecutionHelper {
mode_store_vec, mode_store_vec,
)? { )? {
self.state = SequenceExecutionHelperStates::AwaitingCheckSuccess; self.state = SequenceExecutionHelperStates::AwaitingCheckSuccess;
Ok(SequenceHandlerResult::AwaitingSuccessCheck) Ok(ModeCommandingResult::AwaitingSuccessCheck)
} else if seq_table_value.entries.len() - 1 == sequence_idx { } else if seq_table_value.entries.len() - 1 == sequence_idx {
return Ok(SequenceHandlerResult::SequenceDone); return Ok(ModeCommandingResult::CommandingDone);
} else { } else {
self.current_sequence_index = Some(sequence_idx + 1); self.current_sequence_index = Some(sequence_idx + 1);
return Ok(SequenceHandlerResult::SequenceStepDone); return Ok(ModeCommandingResult::CommandingStepDone);
} }
} }

View File

@ -16,7 +16,7 @@ use satrs::mode_tree::{
use satrs::request::{MessageMetadata, RequestId}; use satrs::request::{MessageMetadata, RequestId};
use satrs::res_code::ResultU16; use satrs::res_code::ResultU16;
use satrs::subsystem::{ use satrs::subsystem::{
ModeDoesNotExistError, SequenceExecutionHelper, SequenceHandlerResult, TargetKeepingResult, ModeCommandingResult, ModeDoesNotExistError, SequenceExecutionHelper, TargetKeepingResult,
}; };
use satrs::{ use satrs::{
mode::{ModeAndSubmode, ModeReply, ModeRequest}, mode::{ModeAndSubmode, ModeReply, ModeRequest},
@ -65,25 +65,34 @@ pub enum ModeTreeHelperState {
#[default] #[default]
Idle, Idle,
TargetKeeping = 1, TargetKeeping = 1,
SequenceCommanding = 2, ModeCommanding = 2,
} }
#[derive(Debug)] #[derive(Debug, Default)]
pub enum ModeTreeHelperResult { pub enum AssemblyHelperResult {
#[default]
Idle,
TargetKeepingViolation(ComponentId),
ModeCommandingDone,
}
#[derive(Debug, Default)]
pub enum SubsystemHelperResult {
#[default]
Idle, Idle,
TargetKeeping(TargetKeepingResult), TargetKeeping(TargetKeepingResult),
SequenceCommanding(SequenceHandlerResult), ModeCommanding(ModeCommandingResult),
} }
impl From<TargetKeepingResult> for ModeTreeHelperResult { impl From<TargetKeepingResult> for SubsystemHelperResult {
fn from(value: TargetKeepingResult) -> Self { fn from(value: TargetKeepingResult) -> Self {
Self::TargetKeeping(value) Self::TargetKeeping(value)
} }
} }
impl From<SequenceHandlerResult> for ModeTreeHelperResult { impl From<ModeCommandingResult> for SubsystemHelperResult {
fn from(value: SequenceHandlerResult) -> Self { fn from(value: ModeCommandingResult) -> Self {
Self::SequenceCommanding(value) Self::ModeCommanding(value)
} }
} }
@ -139,7 +148,7 @@ impl SubsystemCommandingHelper {
request_id: RequestId, request_id: RequestId,
) -> Result<(), ModeDoesNotExistError> { ) -> Result<(), ModeDoesNotExistError> {
self.helper.load(mode, request_id, &self.sequence_tables)?; self.helper.load(mode, request_id, &self.sequence_tables)?;
self.state = ModeTreeHelperState::SequenceCommanding; self.state = ModeTreeHelperState::ModeCommanding;
Ok(()) Ok(())
} }
@ -147,12 +156,12 @@ impl SubsystemCommandingHelper {
&mut self, &mut self,
opt_reply: Option<GenericMessage<ModeReply>>, opt_reply: Option<GenericMessage<ModeReply>>,
req_sender: &impl ModeRequestSender, req_sender: &impl ModeRequestSender,
) -> Result<ModeTreeHelperResult, ModeTreeHelperError> { ) -> Result<SubsystemHelperResult, ModeTreeHelperError> {
if let Some(reply) = opt_reply { if let Some(reply) = opt_reply {
self.handle_mode_reply(&reply); self.handle_mode_reply(&reply);
} }
match self.state { match self.state {
ModeTreeHelperState::Idle => Ok(ModeTreeHelperResult::Idle), ModeTreeHelperState::Idle => Ok(SubsystemHelperResult::Idle),
ModeTreeHelperState::TargetKeeping => { ModeTreeHelperState::TargetKeeping => {
// We check whether the current mode is modelled by a target table first. // We check whether the current mode is modelled by a target table first.
if let Some(target_table) = self.target_tables.0.get(&self.current_mode.mode()) { if let Some(target_table) = self.target_tables.0.get(&self.current_mode.mode()) {
@ -183,9 +192,11 @@ impl SubsystemCommandingHelper {
} }
.into()); .into());
} }
Ok(ModeTreeHelperResult::TargetKeeping(TargetKeepingResult::Ok)) Ok(SubsystemHelperResult::TargetKeeping(
TargetKeepingResult::Ok,
))
} }
ModeTreeHelperState::SequenceCommanding => { ModeTreeHelperState::ModeCommanding => {
let result = self.helper.run( let result = self.helper.run(
&self.sequence_tables, &self.sequence_tables,
req_sender, req_sender,
@ -193,7 +204,7 @@ impl SubsystemCommandingHelper {
)?; )?;
// By default, the helper will automatically transition into the target keeping // By default, the helper will automatically transition into the target keeping
// mode after an executed sequence. // mode after an executed sequence.
if let SequenceHandlerResult::SequenceDone = result { if let ModeCommandingResult::CommandingDone = result {
self.state = ModeTreeHelperState::TargetKeeping; self.state = ModeTreeHelperState::TargetKeeping;
self.current_mode = ModeAndSubmode::new(self.helper.target_mode().unwrap(), 0); self.current_mode = ModeAndSubmode::new(self.helper.target_mode().unwrap(), 0);
} }
@ -202,6 +213,35 @@ impl SubsystemCommandingHelper {
} }
} }
pub fn handle_mode_reply(&mut self, reply: &GenericMessage<ModeReply>) {
if !self.children_mode_store.has_component(reply.sender_id()) {
return;
}
let mut generic_mode_reply_handler =
|sender_id, mode_and_submode: Option<ModeAndSubmode>| {
let still_awating_replies = self
.children_mode_store
.generic_reply_handler(sender_id, mode_and_submode);
if self.state == ModeTreeHelperState::ModeCommanding && !still_awating_replies {
self.helper.confirm_sequence_done();
}
};
match reply.message {
ModeReply::ModeInfo(mode_and_submode) => {
generic_mode_reply_handler(reply.sender_id(), Some(mode_and_submode));
}
ModeReply::ModeReply(mode_and_submode) => {
generic_mode_reply_handler(reply.sender_id(), Some(mode_and_submode));
}
ModeReply::CantReachMode(_) => {
generic_mode_reply_handler(reply.sender_id(), None);
}
ModeReply::WrongMode { reached, .. } => {
generic_mode_reply_handler(reply.sender_id(), Some(reached));
}
};
}
pub fn add_target_and_sequence_table( pub fn add_target_and_sequence_table(
&mut self, &mut self,
mode: Mode, mode: Mode,
@ -219,46 +259,6 @@ impl SubsystemCommandingHelper {
) -> Result<(), TargetNotInModeStoreError> { ) -> Result<(), TargetNotInModeStoreError> {
self.children_mode_store.set_mode(child, mode) self.children_mode_store.set_mode(child, mode)
} }
pub fn handle_mode_reply(&mut self, reply: &GenericMessage<ModeReply>) {
let mut update_mode_store = |target_id, mode_and_submode| {
if !self.children_mode_store.has_component(target_id) {
return;
}
self.children_mode_store
.set_mode_for_contained_component(target_id, mode_and_submode);
match self.state {
ModeTreeHelperState::Idle => (),
ModeTreeHelperState::TargetKeeping => {}
ModeTreeHelperState::SequenceCommanding => {
let mut still_awating_replies = false;
self.children_mode_store.0.iter_mut().for_each(|val| {
if val.id() == target_id {
val.awaiting_reply = false;
}
if val.awaiting_reply {
still_awating_replies = true;
}
});
if !still_awating_replies {
self.helper.confirm_sequence_done();
}
}
}
};
match reply.message {
ModeReply::ModeInfo(mode_and_submode) => {
update_mode_store(reply.sender_id(), mode_and_submode);
}
ModeReply::ModeReply(mode_and_submode) => {
update_mode_store(reply.sender_id(), mode_and_submode);
}
ModeReply::CantReachMode(_) => (),
ModeReply::WrongMode { reached, .. } => {
update_mode_store(reply.sender_id(), reached);
}
};
}
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
@ -473,8 +473,8 @@ impl AcsSubsystem {
.state_machine(mode_reply, &self.mode_node) .state_machine(mode_reply, &self.mode_node)
{ {
Ok(result) => match result { Ok(result) => match result {
ModeTreeHelperResult::Idle => (), SubsystemHelperResult::Idle => (),
ModeTreeHelperResult::TargetKeeping(target_keeping_result) => { SubsystemHelperResult::TargetKeeping(target_keeping_result) => {
match target_keeping_result { match target_keeping_result {
TargetKeepingResult::Ok => todo!(), TargetKeepingResult::Ok => todo!(),
TargetKeepingResult::Violated { fallback_mode } => { TargetKeepingResult::Violated { fallback_mode } => {
@ -486,11 +486,11 @@ impl AcsSubsystem {
} }
} }
} }
ModeTreeHelperResult::SequenceCommanding(sequence_handler_result) => { SubsystemHelperResult::ModeCommanding(sequence_handler_result) => {
match sequence_handler_result { match sequence_handler_result {
SequenceHandlerResult::SequenceDone => (), ModeCommandingResult::CommandingDone => (),
SequenceHandlerResult::SequenceStepDone => (), ModeCommandingResult::CommandingStepDone => (),
SequenceHandlerResult::AwaitingSuccessCheck => (), ModeCommandingResult::AwaitingSuccessCheck => (),
} }
} }
}, },
@ -548,17 +548,15 @@ impl ModeProvider for AcsSubsystem {
} }
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum SubsytemModeError { pub enum SubsystemModeError {
#[error("mode error: {0:?}")] #[error("messaging error: {0:?}")]
Mode(#[from] ModeError), Mode(#[from] ModeError),
#[error("mode does not exist: {0}")] #[error("mode does not exist: {0}")]
ModeDoesNotExist(#[from] ModeDoesNotExistError), ModeDoesNotExist(#[from] ModeDoesNotExistError),
#[error("busy with mode transition")]
Busy,
} }
impl ModeRequestHandler for AcsSubsystem { impl ModeRequestHandler for AcsSubsystem {
type Error = SubsytemModeError; type Error = SubsystemModeError;
fn start_transition( fn start_transition(
&mut self, &mut self,
@ -566,8 +564,8 @@ impl ModeRequestHandler for AcsSubsystem {
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool, forced: bool,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
if !forced && self.subsystem_helper.state == ModeTreeHelperState::SequenceCommanding { if !forced && self.subsystem_helper.state == ModeTreeHelperState::ModeCommanding {
return Err(SubsytemModeError::Busy); return Err(ModeError::Busy.into());
} }
self.mode_requestor_info = Some(requestor); self.mode_requestor_info = Some(requestor);
self.target_mode_and_submode = Some(mode_and_submode); self.target_mode_and_submode = Some(mode_and_submode);
@ -650,13 +648,98 @@ impl ModeRequestHandler for AcsSubsystem {
} }
} }
#[derive(Debug, Default)]
pub struct AssemblyCommandingHelper {
/// The IDs, modes and reply awaition status of all children are tracked in this data
/// structure.
pub children_mode_store: ModeStoreVec,
/// Target mode used for mode commanding.
pub target_mode: Option<ModeAndSubmode>,
pub state: ModeTreeHelperState,
}
impl AssemblyCommandingHelper {
pub fn send_mode_cmd_to_all_children_with_reply_awaition(
&mut self,
request_id: RequestId,
mode_and_submode: ModeAndSubmode,
forced: bool,
mode_req_sender: &impl ModeRequestSender,
) -> Result<(), GenericTargetedMessagingError> {
self.target_mode = Some(mode_and_submode);
for child in self.children_mode_store.0.iter_mut() {
mode_req_sender.send_mode_request(
request_id,
child.id(),
ModeRequest::SetMode {
mode_and_submode,
forced,
},
)?;
child.awaiting_reply = true;
}
Ok(())
}
pub fn count_number_of_children_with_target_mode(&self) -> Option<usize> {
self.target_mode?;
let target_mode = self.target_mode.unwrap();
let mut children_in_target_mode = 0;
for child in self.children_mode_store.0.iter() {
if child.mode_and_submode() == target_mode {
children_in_target_mode += 1;
}
}
Some(children_in_target_mode)
}
pub fn handle_mode_reply(
&mut self,
mode_reply: &GenericMessage<ModeReply>,
) -> AssemblyHelperResult {
if !self
.children_mode_store
.has_component(mode_reply.sender_id())
{
return AssemblyHelperResult::Idle;
}
let mut generic_mode_reply_handler = |mode_and_submode: Option<ModeAndSubmode>| {
let still_awating_replies = self
.children_mode_store
.generic_reply_handler(mode_reply.sender_id(), mode_and_submode);
if self.state == ModeTreeHelperState::TargetKeeping
&& mode_and_submode.is_some()
&& self.target_mode.is_some()
&& mode_and_submode.unwrap() != self.target_mode.unwrap()
{
return AssemblyHelperResult::TargetKeepingViolation(mode_reply.sender_id());
}
if self.state == ModeTreeHelperState::ModeCommanding && !still_awating_replies {
self.state = ModeTreeHelperState::TargetKeeping;
return AssemblyHelperResult::ModeCommandingDone;
}
AssemblyHelperResult::Idle
};
match mode_reply.message {
ModeReply::ModeInfo(mode_and_submode) | ModeReply::ModeReply(mode_and_submode) => {
generic_mode_reply_handler(Some(mode_and_submode))
}
ModeReply::CantReachMode(_result_u16) => generic_mode_reply_handler(None),
ModeReply::WrongMode {
expected: _,
reached,
} => generic_mode_reply_handler(Some(reached)),
}
}
}
// TODO: This assembly requires some helper component to process commands.. Maybe implement it // TODO: This assembly requires some helper component to process commands.. Maybe implement it
// manually first? // manually first?
struct MgmAssembly { struct MgmAssembly {
pub mode_node: ModeRequestorAndHandlerMpscBounded, pub mode_node: ModeRequestorAndHandlerMpscBounded,
pub mode_requestor_info: Option<MessageMetadata>, pub mode_requestor_info: Option<MessageMetadata>,
pub mode_and_submode: ModeAndSubmode, pub mode_and_submode: ModeAndSubmode,
pub target_mode_and_submode: Option<ModeAndSubmode>, pub assembly_helper: AssemblyCommandingHelper,
pub mode_req_mock: ModeRequestHandlerMock, pub mode_req_mock: ModeRequestHandlerMock,
pub mode_reply_mock: ModeReplyHandlerMock, pub mode_reply_mock: ModeReplyHandlerMock,
} }
@ -667,7 +750,7 @@ impl MgmAssembly {
mode_node, mode_node,
mode_requestor_info: None, mode_requestor_info: None,
mode_and_submode: UNKNOWN_MODE, mode_and_submode: UNKNOWN_MODE,
target_mode_and_submode: None, assembly_helper: Default::default(),
mode_req_mock: Default::default(), mode_req_mock: Default::default(),
mode_reply_mock: Default::default(), mode_reply_mock: Default::default(),
} }
@ -688,30 +771,22 @@ impl MgmAssembly {
Ok(()) Ok(())
} }
pub fn check_mode_replies(&mut self) -> Result<(), GenericTargetedMessagingError> { pub fn check_mode_replies(&mut self) -> Result<(), ModeError> {
// TODO: If a transition is active, we need to check whether all children have replied
// and have the correct mode. So we probably need some children list / map similarly to the
// subsystem, which also tracks where a reply is still awaited.
if let Some(reply_and_id) = self.mode_node.try_recv_mode_reply()? { if let Some(reply_and_id) = self.mode_node.try_recv_mode_reply()? {
self.mode_reply_mock.handle_mode_reply(&reply_and_id); self.mode_reply_mock.handle_mode_reply(&reply_and_id);
match reply_and_id.message { match self.assembly_helper.handle_mode_reply(&reply_and_id) {
ModeReply::ModeReply(reply) => { AssemblyHelperResult::Idle => (),
println!( AssemblyHelperResult::TargetKeepingViolation(_id) => {
"TestAssembly: Received mode reply from {:?}, reached: {:?}", // TODO: Check whether enough children are available to keep the mode.
reply_and_id.sender_id(), // Otherwise, we command everything OFF, because we can not keep the mode.
reply
);
} }
ModeReply::CantReachMode(_) => todo!(), AssemblyHelperResult::ModeCommandingDone => {
ModeReply::WrongMode { expected, reached } => { if self.assembly_helper.target_mode.is_some() {
println!( // Complete the mode command.
"TestAssembly: Wrong mode reply from {:?}, reached {:?}, expected {:?}", self.handle_mode_reached(self.mode_requestor_info)?;
reply_and_id.sender_id(), self.mode_and_submode = self.assembly_helper.target_mode.take().unwrap();
reached, }
expected
);
} }
ModeReply::ModeInfo(_mode_and_submode) => {}
} }
} }
Ok(()) Ok(())
@ -753,30 +828,24 @@ impl ModeRequestHandler for MgmAssembly {
mode_and_submode: ModeAndSubmode, mode_and_submode: ModeAndSubmode,
forced: bool, forced: bool,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
// Always accept forced commands and commands to mode OFF.
if self.assembly_helper.target_mode.is_some()
&& !forced
&& mode_and_submode.mode() != DefaultMode::OFF as u32
{
return Err(ModeError::Busy);
}
self.mode_requestor_info = Some(requestor); self.mode_requestor_info = Some(requestor);
self.target_mode_and_submode = Some(mode_and_submode);
self.mode_req_mock self.mode_req_mock
.start_transition(requestor, mode_and_submode, forced) .start_transition(requestor, mode_and_submode, forced)
.unwrap(); .unwrap();
// TODO: Is it correct to simply forward the mode? self.assembly_helper
self.mode_node .send_mode_cmd_to_all_children_with_reply_awaition(
.request_sender_store requestor.request_id(),
.0 mode_and_submode,
.iter() forced,
.for_each(|(_, sender)| { &self.mode_node,
sender )?;
.send(GenericMessage::new(
MessageMetadata::new(
requestor.request_id(),
self.mode_node.local_channel_id_generic(),
),
ModeRequest::SetMode {
mode_and_submode,
forced: false,
},
))
.expect("sending mode request failed");
});
Ok(()) Ok(())
} }