From 5f22a067a56903aae3de745b1a27fdcd64ba380b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 14 Mar 2024 16:37:34 +0100 Subject: [PATCH] we need to constrain somewhere.. --- satrs/src/mode.rs | 21 +++++-- satrs/src/pus/action.rs | 126 +++++++++++++++++++++++++--------------- satrs/src/pus/mod.rs | 4 +- satrs/src/pus/mode.rs | 125 +++++++++++++++++++++++++++++++++++++-- satrs/src/request.rs | 2 +- 5 files changed, 220 insertions(+), 58 deletions(-) diff --git a/satrs/src/mode.rs b/satrs/src/mode.rs index 3a52691..3a099ce 100644 --- a/satrs/src/mode.rs +++ b/satrs/src/mode.rs @@ -26,6 +26,8 @@ pub struct ModeAndSubmode { } impl ModeAndSubmode { + const RAW_LEN: usize = size_of::() + size_of::(); + pub const fn new_mode_only(mode: Mode) -> Self { Self { mode, submode: 0 } } @@ -34,14 +36,10 @@ impl ModeAndSubmode { Self { mode, submode } } - pub fn raw_len() -> usize { - size_of::() + size_of::() - } - pub fn from_be_bytes(buf: &[u8]) -> Result { if buf.len() < 6 { return Err(ByteConversionError::FromSliceTooSmall { - expected: 6, + expected: Self::RAW_LEN, found: buf.len(), }); } @@ -55,6 +53,18 @@ impl ModeAndSubmode { }) } + pub fn to_be_bytes(&self, buf: &mut [u8]) -> Result { + if buf.len() < Self::RAW_LEN { + return Err(ByteConversionError::ToSliceTooSmall { + expected: Self::RAW_LEN, + found: buf.len(), + }); + } + buf[0..size_of::()].copy_from_slice(&self.mode.to_be_bytes()); + buf[size_of::()..Self::RAW_LEN].copy_from_slice(&self.submode.to_be_bytes()); + Ok(Self::RAW_LEN) + } + pub fn mode(&self) -> Mode { self.mode } @@ -111,6 +121,7 @@ pub enum ModeReply { ModeInfo(ModeAndSubmode), /// Reply to a mode request to confirm the commanded mode was reached. ModeReply(ModeAndSubmode), + // Can not reach the commanded mode. Returns the mode which was reached in the end. CantReachMode(ModeAndSubmode), WrongMode { expected: ModeAndSubmode, diff --git a/satrs/src/pus/action.rs b/satrs/src/pus/action.rs index ea2f896..5517117 100644 --- a/satrs/src/pus/action.rs +++ b/satrs/src/pus/action.rs @@ -1,8 +1,8 @@ use crate::{ action::{ActionId, ActionRequest}, params::Params, - request::RequestId, - TargetId, + request::{GenericMessage, RequestId}, + ChannelId, TargetId, }; use super::{verification::VerificationToken, ActiveRequest, ActiveRequestProvider}; @@ -26,13 +26,6 @@ pub struct ActionRequestWithId { pub request: ActionRequest, } -#[derive(Clone, Debug)] -pub struct ActionReplyPusWithIds { - pub request_id: RequestId, - pub action_id: ActionId, - pub reply: ActionReplyPus, -} - /// A reply to an action request, but tailored to the PUS standard verification process. #[non_exhaustive] #[derive(Clone, PartialEq, Debug)] @@ -52,6 +45,35 @@ pub enum ActionReplyPus { }, } +#[derive(Debug, PartialEq, Clone)] +pub struct ActionReplyPusWithActionId { + pub action_id: ActionId, + pub variant: ActionReplyPus, +} + +impl ActionReplyPusWithActionId { + pub fn new(action_id: ActionId, variant: ActionReplyPus) -> Self { + Self { action_id, variant } + } +} + +pub type GenericActionReplyPus = GenericMessage; + +impl GenericActionReplyPus { + pub fn new_action_reply( + request_id: RequestId, + sender_id: ChannelId, + action_id: ActionId, + reply: ActionReplyPus, + ) -> Self { + Self::new( + request_id, + sender_id, + ActionReplyPusWithActionId::new(action_id, reply), + ) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ActiveActionRequest { pub action_id: ActionId, @@ -126,13 +148,13 @@ pub mod std_mod { UserHook, TmSender, ActiveActionRequest, - ActionReplyPusWithIds, + ActionReplyPusWithActionId, >; impl< VerificationReporter: VerificationReportingProvider, ActiveRequestMap: ActiveRequestMapProvider, - UserHook: ReplyHandlerHook, + UserHook: ReplyHandlerHook, TmSender: EcssTmSenderCore, > PusService8ReplyHandler { @@ -162,7 +184,7 @@ pub mod std_mod { /// Main handler function to handle all received action replies. pub fn handle_action_reply( &mut self, - action_reply_with_ids: ActionReplyPusWithIds, + action_reply_with_ids: GenericMessage, time_stamp: &[u8], ) -> Result<(), EcssTmtcError> { let active_req = self @@ -174,7 +196,7 @@ pub mod std_mod { return Ok(()); } let active_req = active_req.unwrap().clone(); - let remove_entry = match action_reply_with_ids.reply { + let remove_entry = match action_reply_with_ids.message.variant { ActionReplyPus::CompletionFailed { error_code, params } => { let fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?; self.verification_reporter @@ -233,7 +255,7 @@ pub mod std_mod { impl< VerificationReporter: VerificationReportingProvider, - UserHook: ReplyHandlerHook, + UserHook: ReplyHandlerHook, TmSender: EcssTmSenderCore, > PusService8ReplyHandler< @@ -303,7 +325,7 @@ mod tests { }; use crate::{ - action::ActionRequestVariant, + action::{ActionReplyVariant, ActionRequestVariant}, params::{self, ParamsRaw, WritableToBeBytes}, pus::{ tests::{ @@ -459,12 +481,12 @@ mod tests { #[derive(Default)] pub struct TestReplyHandlerHook { - pub unexpected_replies: VecDeque, + pub unexpected_replies: VecDeque, pub timeouts: RefCell>, } - impl ReplyHandlerHook for TestReplyHandlerHook { - fn handle_unexpected_reply(&mut self, reply: &ActionReplyPusWithIds) { + impl ReplyHandlerHook for TestReplyHandlerHook { + fn handle_unexpected_reply(&mut self, reply: &GenericActionReplyPus) { self.unexpected_replies.push_back(reply.clone()); } @@ -545,7 +567,7 @@ mod tests { token } - pub fn next_unrequested_reply(&self) -> Option { + pub fn next_unrequested_reply(&self) -> Option { self.handler.user_hook.unexpected_replies.front().cloned() } @@ -623,12 +645,11 @@ mod tests { to self.handler { pub fn request_active(&self, request_id: RequestId) -> bool; - pub fn handle_action_reply( - &mut self, - action_reply_with_ids: ActionReplyPusWithIds, - time_stamp: &[u8], - ) -> Result<(), EcssTmtcError>; - + pub fn handle_action_reply( + &mut self, + action_reply_with_ids: GenericMessage, + time_stamp: &[u8] + ) -> Result<(), EcssTmtcError>; pub fn update_time_from_now(&mut self) -> Result<(), SystemTimeError>; @@ -705,6 +726,7 @@ mod tests { #[test] fn test_reply_handler_completion_success() { let mut reply_testbench = Pus8ReplyTestbench::new(true); + let sender_id = 0x06; let request_id = 0x02; let target_id = 0x05; let action_id = 0x03; @@ -717,11 +739,14 @@ mod tests { Duration::from_millis(1), ); assert!(reply_testbench.request_active(request_id)); - let action_reply = ActionReplyPusWithIds { + let action_reply = GenericMessage::new( request_id, - action_id, - reply: ActionReplyPus::Completed, - }; + sender_id, + ActionReplyPusWithActionId { + action_id, + variant: ActionReplyPus::Completed, + }, + ); reply_testbench .handle_action_reply(action_reply, &[]) .expect("reply handling failure"); @@ -742,19 +767,21 @@ mod tests { token, Duration::from_millis(1), ); - let action_reply = ActionReplyPusWithIds { + let action_reply = GenericActionReplyPus::new_action_reply( request_id, action_id, - reply: ActionReplyPus::StepSuccess { step: 1 }, - }; + action_id, + ActionReplyPus::StepSuccess { step: 1 }, + ); reply_testbench .handle_action_reply(action_reply, &[]) .expect("reply handling failure"); - let action_reply = ActionReplyPusWithIds { + let action_reply = GenericActionReplyPus::new_action_reply( request_id, action_id, - reply: ActionReplyPus::Completed, - }; + action_id, + ActionReplyPus::Completed, + ); reply_testbench .handle_action_reply(action_reply, &[]) .expect("reply handling failure"); @@ -764,6 +791,7 @@ mod tests { #[test] fn test_reply_handler_completion_failure() { let mut reply_testbench = Pus8ReplyTestbench::new(true); + let sender_id = 0x01; let request_id = 0x02; let target_id = 0x05; let action_id = 0x03; @@ -776,14 +804,15 @@ mod tests { Duration::from_millis(1), ); let params_raw = ParamsRaw::U32(params::U32(5)); - let action_reply = ActionReplyPusWithIds { + let action_reply = GenericActionReplyPus::new_action_reply( request_id, + sender_id, action_id, - reply: ActionReplyPus::CompletionFailed { + ActionReplyPus::CompletionFailed { error_code: COMPLETION_ERROR_CODE, params: params_raw.into(), }, - }; + ); reply_testbench .handle_action_reply(action_reply, &[]) .expect("reply handling failure"); @@ -798,6 +827,7 @@ mod tests { #[test] fn test_reply_handler_step_failure() { let mut reply_testbench = Pus8ReplyTestbench::new(false); + let sender_id = 0x01; let request_id = 0x02; let target_id = 0x05; let action_id = 0x03; @@ -809,15 +839,16 @@ mod tests { token, Duration::from_millis(1), ); - let action_reply = ActionReplyPusWithIds { + let action_reply = GenericActionReplyPus::new_action_reply( request_id, + sender_id, action_id, - reply: ActionReplyPus::StepFailed { + ActionReplyPus::StepFailed { error_code: COMPLETION_ERROR_CODE_STEP, step: 2, params: ParamsRaw::U32(crate::params::U32(5)).into(), }, - }; + ); reply_testbench .handle_action_reply(action_reply, &[]) .expect("reply handling failure"); @@ -856,21 +887,24 @@ mod tests { #[test] fn test_unrequested_reply() { let mut reply_testbench = Pus8ReplyTestbench::new(true); + let sender_id = 0x01; let request_id = 0x02; let action_id = 0x03; - let action_reply = ActionReplyPusWithIds { + + let action_reply = GenericActionReplyPus::new_action_reply( request_id, + sender_id, action_id, - reply: ActionReplyPus::Completed, - }; + ActionReplyPus::Completed, + ); reply_testbench .handle_action_reply(action_reply, &[]) .expect("reply handling failure"); let reply = reply_testbench.next_unrequested_reply(); assert!(reply.is_some()); let reply = reply.unwrap(); - assert_eq!(reply.action_id, action_id); + assert_eq!(reply.message.action_id, action_id); assert_eq!(reply.request_id, request_id); - assert_eq!(reply.reply, ActionReplyPus::Completed); + assert_eq!(reply.message.variant, ActionReplyPus::Completed); } } diff --git a/satrs/src/pus/mod.rs b/satrs/src/pus/mod.rs index 87189a4..1bac9df 100644 --- a/satrs/src/pus/mod.rs +++ b/satrs/src/pus/mod.rs @@ -5,7 +5,7 @@ use crate::pool::{StoreAddr, StoreError}; use crate::pus::verification::{TcStateAccepted, TcStateToken, VerificationToken}; use crate::queue::{GenericReceiveError, GenericSendError}; -use crate::request::RequestId; +use crate::request::{GenericMessage, RequestId}; use crate::{ChannelId, TargetId}; use core::fmt::{Display, Formatter}; use core::marker::PhantomData; @@ -332,7 +332,7 @@ impl ActiveRequestProvider for ActiveRequest { /// 2. It exposes callback methods which can be useful to perform custom user operations like /// logging. pub trait ReplyHandlerHook { - fn handle_unexpected_reply(&mut self, reply: &ReplyType); + fn handle_unexpected_reply(&mut self, reply: &GenericMessage); fn timeout_callback(&self, active_request: &ActiveRequestType); fn timeout_error_code(&self) -> ResultU16; } diff --git a/satrs/src/pus/mode.rs b/satrs/src/pus/mode.rs index 47b449e..25a0c98 100644 --- a/satrs/src/pus/mode.rs +++ b/satrs/src/pus/mode.rs @@ -2,9 +2,9 @@ use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::{mode::ModeRequest, TargetId}; +use crate::{mode::ModeReply, request::GenericMessage}; -use super::verification::{TcStateAccepted, VerificationToken}; +pub const MODE_SERVICE_ID: u8 = 200; #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -19,6 +19,8 @@ pub enum Subservice { TmWrongModeReply = 8, } +pub type GenericModeReplyPus = GenericMessage; + #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub mod alloc_mod {} @@ -26,11 +28,30 @@ pub mod alloc_mod {} #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub mod std_mod { + use core::time::Duration; + + use satrs_shared::res_code::ResultU16; + use crate::{ - mode::ModeRequest, - pus::{GenericRoutingError, PusTargetedRequestHandler}, + mode::{ModeReply, ModeRequest}, + pus::{ + verification::{ + self, FailParams, TcStateStarted, VerificationReportingProvider, VerificationToken, + }, + ActiveRequest, ActiveRequestMapProvider, EcssTmSenderCore, EcssTmtcError, + GenericRoutingError, PusServiceReplyHandler, PusTargetedRequestHandler, + ReplyHandlerHook, + }, + TargetId, }; + pub trait ModeReplyHook: ReplyHandlerHook { + fn wrong_mode_result_code(&self) -> ResultU16; + fn can_not_reach_mode_result_code(&self) -> ResultU16; + } + + use super::GenericModeReplyPus; + pub type PusModeServiceRequestHandler< TcReceiver, TmSender, @@ -51,4 +72,100 @@ pub mod std_mod { ModeRequest, RoutingError, >; + + /// Type definition for a PUS mode servicd reply handler which constrains the + /// [PusServiceReplyHandler] active request and reply generics to the [ActiveActionRequest] and + /// [ActionReplyPusWithIds] type. + pub type PusModeServiceReplyHandler< + VerificationReporter, + ActiveRequestMap, + UserHook, + TmSender, + > = PusServiceReplyHandler< + VerificationReporter, + ActiveRequestMap, + UserHook, + TmSender, + ActiveRequest, + ModeReply, + >; + + impl< + VerificationReporter: VerificationReportingProvider, + ActiveRequestMap: ActiveRequestMapProvider, + UserHook: ModeReplyHook, + TmSender: EcssTmSenderCore, + > PusModeServiceReplyHandler + { + /// Helper method to register a recently routed action request. + pub fn add_routed_mode_request( + &mut self, + request_id: verification::RequestId, + target_id: TargetId, + token: VerificationToken, + timeout: Duration, + ) { + self.active_request_map.insert( + &request_id.into(), + ActiveRequest { + target_id, + token, + start_time: self.current_time, + timeout, + }, + ) + } + + /// Main handler function to handle all received action replies. + pub fn handle_mode_reply( + &mut self, + mode_reply_with_id: &GenericModeReplyPus, + time_stamp: &[u8], + ) -> Result<(), EcssTmtcError> { + let active_req = self.active_request_map.get(mode_reply_with_id.request_id); + if active_req.is_none() { + self.user_hook.handle_unexpected_reply(mode_reply_with_id); + return Ok(()); + } + let active_req = active_req.unwrap().clone(); + let remove_entry = match mode_reply_with_id.message { + ModeReply::ModeReply(reply) => { + // TODO: Send dedicated TM to send mode information. + // TODO: Generate TM. Service ID from hook, subservice ID is fixed for + // framework purposes, APID can be retrieved from Request ID. + + self.verification_reporter + .completion_success(active_req.token, time_stamp) + .map_err(|e| e.0)?; + true + } + ModeReply::CantReachMode(reached_mode) => { + let fail_data_len = reached_mode.to_be_bytes(&mut self.fail_data_buf)?; + self.verification_reporter + .completion_failure( + active_req.token, + FailParams::new( + time_stamp, + &self.user_hook.can_not_reach_mode_result_code(), + &self.fail_data_buf[0..fail_data_len], + ), + ) + .map_err(|e| e.0)?; + true + } + ModeReply::WrongMode { expected, reached } => { + // TODO: Generate completion failure with appropriate result code and reached + // mode as context information. + // self.verification_reporter.completion_success(active_req.token, time_stamp); + true + } + _ => true, + }; + if remove_entry { + self.active_request_map + .remove(mode_reply_with_id.request_id); + } + Ok(()) + } + } } diff --git a/satrs/src/request.rs b/satrs/src/request.rs index 995b55d..28ac95c 100644 --- a/satrs/src/request.rs +++ b/satrs/src/request.rs @@ -88,7 +88,7 @@ impl fmt::Display for TargetAndApidId { /// Generic message type which is associated with a sender using a [ChannelId] and associated /// with a request using a [RequestId]. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct GenericMessage { pub sender_id: ChannelId,