From a5e5a1a5a07bb7b5eb66156082a5519d67e25fd0 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 13 Feb 2024 18:19:45 +0100 Subject: [PATCH] First PUS handler abstraction with request mapping This is the first attempt of a generic PUS service abstraction where the PUS telecommands need to be converted into targetted requests. --- satrs-example/src/acs.rs | 9 +- satrs-example/src/config.rs | 16 +- satrs-example/src/lib.rs | 58 ------ satrs-example/src/main.rs | 19 +- satrs-example/src/pus/action.rs | 236 +++++++++-------------- satrs-example/src/pus/hk.rs | 319 ++++++++++++++++---------------- satrs-example/src/pus/mod.rs | 63 ++++++- satrs-example/src/queue.rs | 45 +++++ satrs-example/src/requests.rs | 60 +++++- satrs/src/action.rs | 33 +++- satrs/src/hk.rs | 28 ++- satrs/src/lib.rs | 6 +- satrs/src/mode.rs | 3 +- satrs/src/objects.rs | 3 +- satrs/src/pus/action.rs | 115 ++++++++++++ satrs/src/pus/hk.rs | 109 +++++++++++ satrs/src/pus/mod.rs | 83 ++++----- satrs/src/queue.rs | 49 +++++ satrs/src/request.rs | 86 ++++++++- satrs/src/tmtc/mod.rs | 2 - 20 files changed, 884 insertions(+), 458 deletions(-) create mode 100644 satrs-example/src/queue.rs create mode 100644 satrs/src/pus/action.rs create mode 100644 satrs/src/queue.rs diff --git a/satrs-example/src/acs.rs b/satrs-example/src/acs.rs index dc7763b..d32f6a3 100644 --- a/satrs-example/src/acs.rs +++ b/satrs-example/src/acs.rs @@ -3,6 +3,7 @@ use std::sync::mpsc::{self, TryRecvError}; use log::{info, warn}; use satrs::pus::verification::VerificationReporterWithSender; use satrs::pus::{EcssTmSender, PusTmWrapper}; +use satrs::request::TargetAndApidId; use satrs::spacepackets::ecss::hk::Subservice as HkSubservice; use satrs::{ hk::HkRequest, @@ -70,12 +71,12 @@ impl AcsTask { "ACS thread: Received HK request {:?}", request.targeted_request ); + let target_and_apid_id = TargetAndApidId::from(request.targeted_request.target_id); match request.targeted_request.request { Request::Hk(hk_req) => match hk_req { - HkRequest::OneShot(unique_id) => self.handle_hk_request( - request.targeted_request.target_id_with_apid.target_id(), - unique_id, - ), + HkRequest::OneShot(unique_id) => { + self.handle_hk_request(target_and_apid_id.target(), unique_id) + } HkRequest::Enable(_) => {} HkRequest::Disable(_) => {} HkRequest::ModifyCollectionInterval(_, _) => {} diff --git a/satrs-example/src/config.rs b/satrs-example/src/config.rs index 4cc960f..9d04403 100644 --- a/satrs-example/src/config.rs +++ b/satrs-example/src/config.rs @@ -48,7 +48,11 @@ pub mod tmtc_err { #[resultcode] pub const PUS_SERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 2); #[resultcode] - pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 3); + pub const PUS_SUBSERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 3); + #[resultcode] + pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 4); + #[resultcode] + pub const ROUTING_ERROR: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 5); #[resultcode( info = "Not enough data inside the TC application data field. Optionally includes: \ @@ -60,6 +64,9 @@ pub mod tmtc_err { pub const TMTC_RESULTS: &[ResultU16Info] = &[ INVALID_PUS_SERVICE_EXT, INVALID_PUS_SUBSERVICE_EXT, + PUS_SERVICE_NOT_IMPLEMENTED_EXT, + UNKNOWN_TARGET_ID_EXT, + ROUTING_ERROR_EXT, NOT_ENOUGH_APP_DATA_EXT, ]; } @@ -76,6 +83,13 @@ pub mod hk_err { pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Hk as u8, 2); #[resultcode] pub const COLLECTION_INTERVAL_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 3); + + pub const HK_ERR_RESULTS: &[ResultU16Info] = &[ + TARGET_ID_MISSING_EXT, + UNKNOWN_TARGET_ID_EXT, + UNKNOWN_TARGET_ID_EXT, + COLLECTION_INTERVAL_MISSING_EXT, + ]; } #[allow(clippy::enum_variant_names)] diff --git a/satrs-example/src/lib.rs b/satrs-example/src/lib.rs index dac8ab0..ef68c36 100644 --- a/satrs-example/src/lib.rs +++ b/satrs-example/src/lib.rs @@ -1,59 +1 @@ -use derive_new::new; -use satrs::spacepackets::ecss::tc::IsPusTelecommand; -use satrs::spacepackets::ecss::PusPacket; -use satrs::spacepackets::{ByteConversionError, CcsdsPacket}; -use satrs::tmtc::TargetId; -use std::fmt; -use thiserror::Error; - pub mod config; - -pub type Apid = u16; - -#[derive(Debug, Error)] -pub enum TargetIdCreationError { - #[error("byte conversion")] - ByteConversion(#[from] ByteConversionError), - #[error("not enough app data to generate target ID")] - NotEnoughAppData(usize), -} - -// TODO: can these stay pub? -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, new)] -pub struct TargetIdWithApid { - pub apid: Apid, - pub target: TargetId, -} - -impl fmt::Display for TargetIdWithApid { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}, {}", self.apid, self.target) - } -} - -impl TargetIdWithApid { - pub fn apid(&self) -> Apid { - self.apid - } - pub fn target_id(&self) -> TargetId { - self.target - } -} - -impl TargetIdWithApid { - pub fn from_tc( - tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand), - ) -> Result { - if tc.user_data().len() < 4 { - return Err(ByteConversionError::FromSliceTooSmall { - found: tc.user_data().len(), - expected: 8, - } - .into()); - } - Ok(Self { - apid: tc.apid(), - target: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()), - }) - } -} diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index 32dbfcb..76f2ffd 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -17,6 +17,7 @@ use log::info; use pus::test::create_test_service_dynamic; use satrs::hal::std::tcp_server::ServerConfig; use satrs::hal::std::udp_server::UdpTcServer; +use satrs::request::TargetAndApidId; use satrs::tmtc::tm_helper::SharedTmPool; use satrs_example::config::pool::{create_sched_tc_pool, create_static_pools}; use satrs_example::config::tasks::{ @@ -35,7 +36,7 @@ use crate::pus::hk::{create_hk_service_dynamic, create_hk_service_static}; use crate::pus::scheduler::{create_scheduler_service_dynamic, create_scheduler_service_static}; use crate::pus::test::create_test_service_static; use crate::pus::{PusReceiver, PusTcMpscRouter}; -use crate::requests::RequestWithToken; +use crate::requests::{GenericRequestRouter, RequestWithToken}; use crate::tcp::{SyncTcpTmSource, TcpTask}; use crate::tmtc::{ PusTcSourceProviderSharedPool, SharedTcPool, TcSourceTaskDynamic, TcSourceTaskStatic, @@ -45,10 +46,8 @@ use satrs::pus::event_man::EventRequestWithToken; use satrs::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender}; use satrs::pus::{EcssTmSender, MpscTmAsVecSender, MpscTmInSharedPoolSender}; use satrs::spacepackets::{time::cds::TimeProvider, time::TimeWriter}; -use satrs::tmtc::{CcsdsDistributor, TargetId}; +use satrs::tmtc::CcsdsDistributor; use satrs::ChannelId; -use satrs_example::TargetIdWithApid; -use std::collections::HashMap; use std::net::{IpAddr, SocketAddr}; use std::sync::mpsc::{self, channel}; use std::sync::{Arc, RwLock}; @@ -82,11 +81,11 @@ fn static_tmtc_pool_main() { tm_funnel_tx.clone(), )); - let acs_target_id = TargetIdWithApid::new(PUS_APID, RequestTargetId::AcsSubsystem as TargetId); + let acs_target_id = TargetAndApidId::new(PUS_APID, RequestTargetId::AcsSubsystem as u32); let (acs_thread_tx, acs_thread_rx) = channel::(); // Some request are targetable. This map is used to retrieve sender handles based on a target ID. - let mut request_map = HashMap::new(); - request_map.insert(acs_target_id, acs_thread_tx); + let mut request_map = GenericRequestRouter::default(); + request_map.0.insert(acs_target_id.into(), acs_thread_tx); // This helper structure is used by all telecommand providers which need to send telecommands // to the TC source. @@ -310,11 +309,11 @@ fn dyn_tmtc_pool_main() { tm_funnel_tx.clone(), )); - let acs_target_id = TargetIdWithApid::new(PUS_APID, RequestTargetId::AcsSubsystem as TargetId); + let acs_target_id = TargetAndApidId::new(PUS_APID, RequestTargetId::AcsSubsystem as u32); let (acs_thread_tx, acs_thread_rx) = channel::(); // Some request are targetable. This map is used to retrieve sender handles based on a target ID. - let mut request_map = HashMap::new(); - request_map.insert(acs_target_id, acs_thread_tx); + let mut request_map = GenericRequestRouter::default(); + request_map.0.insert(acs_target_id.into(), acs_thread_tx); let tc_source = PusTcSourceProviderDynamic(tc_source_tx); diff --git a/satrs-example/src/pus/action.rs b/satrs-example/src/pus/action.rs index 2d9dfe6..f383c68 100644 --- a/satrs-example/src/pus/action.rs +++ b/satrs-example/src/pus/action.rs @@ -1,23 +1,74 @@ -use crate::requests::{Request, RequestWithToken}; use log::{error, warn}; use satrs::action::ActionRequest; use satrs::pool::{SharedStaticMemoryPool, StoreAddr}; +use satrs::pus::action::{PusActionToRequestConverter, PusService8ActionHandler}; use satrs::pus::verification::{ FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationToken, }; use satrs::pus::{ EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, - EcssTcReceiver, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, - PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper, + MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult, + PusPacketHandlingError, PusServiceHelper, }; +use satrs::request::TargetAndApidId; use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::PusPacket; use satrs::tmtc::tm_helper::SharedTmPool; -use satrs::ChannelId; +use satrs::{ChannelId, TargetId}; use satrs_example::config::{tmtc_err, TcReceiverId, TmSenderId, PUS_APID}; -use satrs_example::TargetIdWithApid; -use std::collections::HashMap; -use std::sync::mpsc::{self, Sender}; +use std::sync::mpsc::{self}; + +use crate::requests::GenericRequestRouter; + +use super::GenericRoutingErrorHandler; + +#[derive(Default)] +pub struct ExampleActionRequestConverter {} + +impl PusActionToRequestConverter for ExampleActionRequestConverter { + type Error = PusPacketHandlingError; + + fn convert( + &mut self, + token: VerificationToken, + tc: &PusTcReader, + time_stamp: &[u8], + verif_reporter: &mut VerificationReporterWithSender, + ) -> Result<(TargetId, ActionRequest), Self::Error> { + let subservice = tc.subservice(); + let user_data = tc.user_data(); + if user_data.len() < 8 { + verif_reporter + .start_failure( + token, + FailParams::new(Some(time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None), + ) + .expect("Sending start failure failed"); + return Err(PusPacketHandlingError::NotEnoughAppData( + "Expected at least 4 bytes".into(), + )); + } + let target_id = TargetAndApidId::from_pus_tc(tc).unwrap(); + let action_id = u32::from_be_bytes(user_data[4..8].try_into().unwrap()); + if subservice == 128 { + Ok(( + target_id.raw(), + ActionRequest::ActionIdAndVecData { + action_id, + data: user_data[8..].to_vec(), + }, + )) + } else { + verif_reporter + .start_failure( + token, + FailParams::new(Some(time_stamp), &tmtc_err::INVALID_PUS_SUBSERVICE, None), + ) + .expect("Sending start failure failed"); + Err(PusPacketHandlingError::InvalidSubservice(subservice)) + } + } +} pub fn create_action_service_static( shared_tm_store: SharedTmPool, @@ -25,7 +76,7 @@ pub fn create_action_service_static( verif_reporter: VerificationReporterWithSender, tc_pool: SharedStaticMemoryPool, pus_action_rx: mpsc::Receiver, - request_map: HashMap>, + action_router: GenericRequestRouter, ) -> Pus8Wrapper { let action_srv_tm_sender = MpscTmInSharedPoolSender::new( TmSenderId::PusAction as ChannelId, @@ -39,12 +90,16 @@ pub fn create_action_service_static( pus_action_rx, ); let pus_8_handler = PusService8ActionHandler::new( - Box::new(action_srv_receiver), - Box::new(action_srv_tm_sender), - PUS_APID, - verif_reporter.clone(), - EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048), - request_map.clone(), + PusServiceHelper::new( + Box::new(action_srv_receiver), + Box::new(action_srv_tm_sender), + PUS_APID, + verif_reporter.clone(), + EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048), + ), + ExampleActionRequestConverter::default(), + action_router, + GenericRoutingErrorHandler::<8>::default(), ); Pus8Wrapper { pus_8_handler } } @@ -53,7 +108,7 @@ pub fn create_action_service_dynamic( tm_funnel_tx: mpsc::Sender>, verif_reporter: VerificationReporterWithSender, pus_action_rx: mpsc::Receiver, - request_map: HashMap>, + action_router: GenericRequestRouter, ) -> Pus8Wrapper { let action_srv_tm_sender = MpscTmAsVecSender::new( TmSenderId::PusAction as ChannelId, @@ -66,146 +121,27 @@ pub fn create_action_service_dynamic( pus_action_rx, ); let pus_8_handler = PusService8ActionHandler::new( - Box::new(action_srv_receiver), - Box::new(action_srv_tm_sender), - PUS_APID, - verif_reporter.clone(), - EcssTcInVecConverter::default(), - request_map.clone(), + PusServiceHelper::new( + Box::new(action_srv_receiver), + Box::new(action_srv_tm_sender), + PUS_APID, + verif_reporter.clone(), + EcssTcInVecConverter::default(), + ), + ExampleActionRequestConverter::default(), + action_router, + GenericRoutingErrorHandler::<8>::default(), ); Pus8Wrapper { pus_8_handler } } -pub struct PusService8ActionHandler { - service_helper: PusServiceHelper, - request_handlers: HashMap>, -} - -impl PusService8ActionHandler { - pub fn new( - tc_receiver: Box, - tm_sender: Box, - tm_apid: u16, - verification_handler: VerificationReporterWithSender, - tc_in_mem_converter: TcInMemConverter, - request_handlers: HashMap>, - ) -> Self { - Self { - service_helper: PusServiceHelper::new( - tc_receiver, - tm_sender, - tm_apid, - verification_handler, - tc_in_mem_converter, - ), - request_handlers, - } - } - - fn handle_action_request_with_id( - &self, - token: VerificationToken, - tc: &PusTcReader, - time_stamp: &[u8], - ) -> Result<(), PusPacketHandlingError> { - let user_data = tc.user_data(); - if user_data.len() < 8 { - self.service_helper - .common - .verification_handler - .borrow_mut() - .start_failure( - token, - FailParams::new(Some(time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None), - ) - .expect("Sending start failure failed"); - return Err(PusPacketHandlingError::NotEnoughAppData( - "Expected at least 4 bytes".into(), - )); - } - //let target_id = u32::from_be_bytes(user_data[0..4].try_into().unwrap()); - let target_id = TargetIdWithApid::from_tc(tc).unwrap(); - let action_id = u32::from_be_bytes(user_data[4..8].try_into().unwrap()); - if let Some(sender) = self.request_handlers.get(&target_id) { - sender - .send(RequestWithToken::new( - target_id, - Request::Action(ActionRequest::ActionIdAndVecData(( - action_id, - Vec::from(&user_data[8..]), - ))), - token, - )) - .expect("Forwarding action request failed"); - } else { - let mut fail_data: [u8; 4] = [0; 4]; - fail_data.copy_from_slice(&target_id.target.to_be_bytes()); - self.service_helper - .common - .verification_handler - .borrow_mut() - .start_failure( - token, - FailParams::new( - Some(time_stamp), - &tmtc_err::UNKNOWN_TARGET_ID, - Some(&fail_data), - ), - ) - .expect("Sending start failure failed"); - return Err(PusPacketHandlingError::Other(format!( - "Unknown target ID {target_id}" - ))); - } - Ok(()) - } - - fn handle_one_tc(&mut self) -> Result { - let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; - if possible_packet.is_none() { - return Ok(PusPacketHandlerResult::Empty); - } - let ecss_tc_and_token = possible_packet.unwrap(); - self.service_helper - .tc_in_mem_converter - .cache_ecss_tc_in_memory(&ecss_tc_and_token.tc_in_memory)?; - let tc = PusTcReader::new(self.service_helper.tc_in_mem_converter.tc_slice_raw())?.0; - let subservice = tc.subservice(); - let mut partial_error = None; - let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error); - match subservice { - 128 => { - self.handle_action_request_with_id(ecss_tc_and_token.token, &tc, &time_stamp)?; - } - _ => { - let fail_data = [subservice]; - self.service_helper - .common - .verification_handler - .get_mut() - .start_failure( - ecss_tc_and_token.token, - FailParams::new( - Some(&time_stamp), - &tmtc_err::INVALID_PUS_SUBSERVICE, - Some(&fail_data), - ), - ) - .expect("Sending start failure failed"); - return Err(PusPacketHandlingError::InvalidSubservice(subservice)); - } - } - if let Some(partial_error) = partial_error { - return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess( - partial_error, - )); - } - Ok(PusPacketHandlerResult::RequestHandled) - } -} - pub struct Pus8Wrapper { - pub(crate) pus_8_handler: PusService8ActionHandler, + pub(crate) pus_8_handler: PusService8ActionHandler< + TcInMemConverter, + ExampleActionRequestConverter, + GenericRequestRouter, + GenericRoutingErrorHandler<8>, + >, } impl Pus8Wrapper { diff --git a/satrs-example/src/pus/hk.rs b/satrs-example/src/pus/hk.rs index 41c98b9..f3a9cf9 100644 --- a/satrs-example/src/pus/hk.rs +++ b/satrs-example/src/pus/hk.rs @@ -1,122 +1,46 @@ -use crate::requests::{Request, RequestWithToken}; use log::{error, warn}; use satrs::hk::{CollectionIntervalFactor, HkRequest}; use satrs::pool::{SharedStaticMemoryPool, StoreAddr}; +use satrs::pus::hk::{PusHkToRequestConverter, PusService3HkHandler}; use satrs::pus::verification::{ - FailParams, StdVerifReporterWithSender, VerificationReporterWithSender, + FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationToken, }; use satrs::pus::{ EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, - EcssTcReceiver, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, - PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper, + MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult, + PusPacketHandlingError, PusServiceHelper, }; +use satrs::request::TargetAndApidId; +use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::{hk, PusPacket}; use satrs::tmtc::tm_helper::SharedTmPool; -use satrs::ChannelId; +use satrs::{ChannelId, TargetId}; use satrs_example::config::{hk_err, tmtc_err, TcReceiverId, TmSenderId, PUS_APID}; -use satrs_example::TargetIdWithApid; -use std::collections::HashMap; -use std::sync::mpsc::{self, Sender}; +use std::sync::mpsc::{self}; -pub fn create_hk_service_static( - shared_tm_store: SharedTmPool, - tm_funnel_tx: mpsc::Sender, - verif_reporter: VerificationReporterWithSender, - tc_pool: SharedStaticMemoryPool, - pus_hk_rx: mpsc::Receiver, - request_map: HashMap>, -) -> Pus3Wrapper { - let hk_srv_tm_sender = MpscTmInSharedPoolSender::new( - TmSenderId::PusHk as ChannelId, - "PUS_3_TM_SENDER", - shared_tm_store.clone(), - tm_funnel_tx.clone(), - ); - let hk_srv_receiver = - MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx); - let pus_3_handler = PusService3HkHandler::new( - Box::new(hk_srv_receiver), - Box::new(hk_srv_tm_sender), - PUS_APID, - verif_reporter.clone(), - EcssTcInSharedStoreConverter::new(tc_pool, 2048), - request_map, - ); - Pus3Wrapper { pus_3_handler } -} +use crate::requests::GenericRequestRouter; -pub fn create_hk_service_dynamic( - tm_funnel_tx: mpsc::Sender>, - verif_reporter: VerificationReporterWithSender, - pus_hk_rx: mpsc::Receiver, - request_map: HashMap>, -) -> Pus3Wrapper { - let hk_srv_tm_sender = MpscTmAsVecSender::new( - TmSenderId::PusHk as ChannelId, - "PUS_3_TM_SENDER", - tm_funnel_tx.clone(), - ); - let hk_srv_receiver = - MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx); - let pus_3_handler = PusService3HkHandler::new( - Box::new(hk_srv_receiver), - Box::new(hk_srv_tm_sender), - PUS_APID, - verif_reporter.clone(), - EcssTcInVecConverter::default(), - request_map, - ); - Pus3Wrapper { pus_3_handler } -} +use super::GenericRoutingErrorHandler; -pub struct PusService3HkHandler { - psb: PusServiceHelper, - request_handlers: HashMap>, -} +#[derive(Default)] +pub struct ExampleHkRequestConverter {} -impl PusService3HkHandler { - pub fn new( - tc_receiver: Box, - tm_sender: Box, - tm_apid: u16, - verification_handler: StdVerifReporterWithSender, - tc_in_mem_converter: TcInMemConverter, - request_handlers: HashMap>, - ) -> Self { - Self { - psb: PusServiceHelper::new( - tc_receiver, - tm_sender, - tm_apid, - verification_handler, - tc_in_mem_converter, - ), - request_handlers, - } - } +impl PusHkToRequestConverter for ExampleHkRequestConverter { + type Error = PusPacketHandlingError; - fn handle_one_tc(&mut self) -> Result { - let possible_packet = self.psb.retrieve_and_accept_next_packet()?; - if possible_packet.is_none() { - return Ok(PusPacketHandlerResult::Empty); - } - let ecss_tc_and_token = possible_packet.unwrap(); - let tc = self - .psb - .tc_in_mem_converter - .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; - let subservice = tc.subservice(); - let mut partial_error = None; - let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error); + fn convert( + &mut self, + token: VerificationToken, + tc: &PusTcReader, + time_stamp: &[u8], + verif_reporter: &mut VerificationReporterWithSender, + ) -> Result<(TargetId, HkRequest), Self::Error> { let user_data = tc.user_data(); if user_data.is_empty() { - self.psb - .common - .verification_handler - .borrow_mut() + verif_reporter .start_failure( - ecss_tc_and_token.token, - FailParams::new(Some(&time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None), + token, + FailParams::new(Some(time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None), ) .expect("Sending start failure TM failed"); return Err(PusPacketHandlingError::NotEnoughAppData( @@ -129,84 +53,153 @@ impl PusService3HkHandler { + HkRequest::Enable(unique_id) + } + hk::Subservice::TcDisableHkGeneration | hk::Subservice::TcDisableDiagGeneration => { + HkRequest::Disable(unique_id) + } + hk::Subservice::TcReportHkReportStructures => todo!(), + hk::Subservice::TmHkPacket => todo!(), + hk::Subservice::TcGenerateOneShotHk | hk::Subservice::TcGenerateOneShotDiag => { + HkRequest::OneShot(unique_id) + } + hk::Subservice::TcModifyDiagCollectionInterval + | hk::Subservice::TcModifyHkCollectionInterval => { + if user_data.len() < 12 { + verif_reporter + .start_failure( + token, + FailParams::new( + Some(time_stamp), + &tmtc_err::NOT_ENOUGH_APP_DATA, + None, + ), + ) + .expect("Sending start failure TM failed"); + return Err(PusPacketHandlingError::NotEnoughAppData( + "expected at least 8 bytes of app data".into(), + )); + } + HkRequest::ModifyCollectionInterval( + unique_id, + CollectionIntervalFactor::from_be_bytes( + user_data[8..12].try_into().unwrap(), ), ) - .expect("Sending start failure TM failed"); - return Err(PusPacketHandlingError::NotEnoughAppData( - "Collection interval missing".into(), - )); - } - send_request( - target_id, - HkRequest::ModifyCollectionInterval( - unique_id, - CollectionIntervalFactor::from_be_bytes(user_data[8..12].try_into().unwrap()), - ), - ); - } - Ok(PusPacketHandlerResult::RequestHandled) + } + _ => { + verif_reporter + .start_failure( + token, + FailParams::new( + Some(time_stamp), + &tmtc_err::PUS_SUBSERVICE_NOT_IMPLEMENTED, + Some(&[subservice]), + ), + ) + .expect("Sending start failure TM failed"); + return Err(PusPacketHandlingError::InvalidSubservice(subservice)); + } + }, + )) } } +pub fn create_hk_service_static( + shared_tm_store: SharedTmPool, + tm_funnel_tx: mpsc::Sender, + verif_reporter: VerificationReporterWithSender, + tc_pool: SharedStaticMemoryPool, + pus_hk_rx: mpsc::Receiver, + request_router: GenericRequestRouter, +) -> Pus3Wrapper { + let hk_srv_tm_sender = MpscTmInSharedPoolSender::new( + TmSenderId::PusHk as ChannelId, + "PUS_3_TM_SENDER", + shared_tm_store.clone(), + tm_funnel_tx.clone(), + ); + let hk_srv_receiver = + MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx); + let pus_3_handler = PusService3HkHandler::new( + PusServiceHelper::new( + Box::new(hk_srv_receiver), + Box::new(hk_srv_tm_sender), + PUS_APID, + verif_reporter.clone(), + EcssTcInSharedStoreConverter::new(tc_pool, 2048), + ), + ExampleHkRequestConverter::default(), + request_router, + GenericRoutingErrorHandler::default(), + ); + Pus3Wrapper { pus_3_handler } +} + +pub fn create_hk_service_dynamic( + tm_funnel_tx: mpsc::Sender>, + verif_reporter: VerificationReporterWithSender, + pus_hk_rx: mpsc::Receiver, + request_router: GenericRequestRouter, +) -> Pus3Wrapper { + let hk_srv_tm_sender = MpscTmAsVecSender::new( + TmSenderId::PusHk as ChannelId, + "PUS_3_TM_SENDER", + tm_funnel_tx.clone(), + ); + let hk_srv_receiver = + MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx); + let pus_3_handler = PusService3HkHandler::new( + PusServiceHelper::new( + Box::new(hk_srv_receiver), + Box::new(hk_srv_tm_sender), + PUS_APID, + verif_reporter.clone(), + EcssTcInVecConverter::default(), + ), + ExampleHkRequestConverter::default(), + request_router, + GenericRoutingErrorHandler::default(), + ); + Pus3Wrapper { pus_3_handler } +} + pub struct Pus3Wrapper { - pub(crate) pus_3_handler: PusService3HkHandler, + pub(crate) pus_3_handler: PusService3HkHandler< + TcInMemConverter, + ExampleHkRequestConverter, + GenericRequestRouter, + GenericRoutingErrorHandler<3>, + >, } impl Pus3Wrapper { diff --git a/satrs-example/src/pus/mod.rs b/satrs-example/src/pus/mod.rs index ec63abe..ef2853f 100644 --- a/satrs-example/src/pus/mod.rs +++ b/satrs-example/src/pus/mod.rs @@ -1,7 +1,9 @@ use crate::tmtc::MpscStoreAndSendError; use log::warn; use satrs::pus::verification::{FailParams, StdVerifReporterWithSender}; -use satrs::pus::{EcssTcAndToken, PusPacketHandlerResult, TcInMemory}; +use satrs::pus::{ + EcssTcAndToken, GenericRoutingError, PusPacketHandlerResult, PusRoutingErrorHandler, TcInMemory, +}; use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::PusServiceId; use satrs::spacepackets::time::cds::TimeProvider; @@ -151,3 +153,62 @@ impl PusReceiver { Ok(PusPacketHandlerResult::RequestHandled) } } + +#[derive(Default)] +pub struct GenericRoutingErrorHandler {} + +impl PusRoutingErrorHandler for GenericRoutingErrorHandler { + type Error = satrs::pus::GenericRoutingError; + + fn handle_error( + &self, + target_id: satrs::TargetId, + token: satrs::pus::verification::VerificationToken< + satrs::pus::verification::TcStateAccepted, + >, + _tc: &PusTcReader, + error: Self::Error, + time_stamp: &[u8], + verif_reporter: &mut satrs::pus::verification::VerificationReporterWithSender, + ) { + warn!("Routing request for service {SERVICE_ID} failed: {error:?}"); + match error { + GenericRoutingError::UnknownTargetId(id) => { + let mut fail_data: [u8; 8] = [0; 8]; + fail_data.copy_from_slice(&id.to_be_bytes()); + verif_reporter + .start_failure( + token, + FailParams::new( + Some(time_stamp), + &tmtc_err::UNKNOWN_TARGET_ID, + Some(&fail_data), + ), + ) + .expect("Sending start failure failed"); + } + GenericRoutingError::SendError(_) => { + let mut fail_data: [u8; 8] = [0; 8]; + fail_data.copy_from_slice(&target_id.to_be_bytes()); + verif_reporter + .start_failure( + token, + FailParams::new( + Some(time_stamp), + &tmtc_err::ROUTING_ERROR, + Some(&fail_data), + ), + ) + .expect("Sending start failure failed"); + } + GenericRoutingError::NotEnoughAppData => { + verif_reporter + .start_failure( + token, + FailParams::new(Some(time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None), + ) + .expect("Sending start failure failed"); + } + } + } +} diff --git a/satrs-example/src/queue.rs b/satrs-example/src/queue.rs new file mode 100644 index 0000000..65e2fde --- /dev/null +++ b/satrs-example/src/queue.rs @@ -0,0 +1,45 @@ +/// Generic error type for sending something via a message queue. +#[derive(Debug, Copy, Clone)] +pub enum GenericSendError { + RxDisconnected, + QueueFull(Option), +} + +impl Display for GenericSendError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + GenericSendError::RxDisconnected => { + write!(f, "rx side has disconnected") + } + GenericSendError::QueueFull(max_cap) => { + write!(f, "queue with max capacity of {max_cap:?} is full") + } + } + } +} + +#[cfg(feature = "std")] +impl Error for GenericSendError {} + +/// Generic error type for sending something via a message queue. +#[derive(Debug, Copy, Clone)] +pub enum GenericRecvError { + Empty, + TxDisconnected, +} + +impl Display for GenericRecvError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Self::TxDisconnected => { + write!(f, "tx side has disconnected") + } + Self::Empty => { + write!(f, "nothing to receive") + } + } + } +} + +#[cfg(feature = "std")] +impl Error for GenericRecvError {} diff --git a/satrs-example/src/requests.rs b/satrs-example/src/requests.rs index 9d9da1b..6703d93 100644 --- a/satrs-example/src/requests.rs +++ b/satrs-example/src/requests.rs @@ -1,9 +1,16 @@ +use std::collections::HashMap; +use std::sync::mpsc; + use derive_new::new; use satrs::action::ActionRequest; use satrs::hk::HkRequest; use satrs::mode::ModeRequest; +use satrs::pus::action::PusActionRequestRouter; +use satrs::pus::hk::PusHkRequestRouter; use satrs::pus::verification::{TcStateAccepted, VerificationToken}; -use satrs_example::TargetIdWithApid; +use satrs::pus::GenericRoutingError; +use satrs::queue::GenericSendError; +use satrs::TargetId; #[allow(dead_code)] #[derive(Clone, Eq, PartialEq, Debug)] @@ -16,7 +23,7 @@ pub enum Request { #[derive(Clone, Eq, PartialEq, Debug, new)] pub struct TargetedRequest { - pub(crate) target_id_with_apid: TargetIdWithApid, + pub(crate) target_id: TargetId, pub(crate) request: Request, } @@ -28,7 +35,7 @@ pub struct RequestWithToken { impl RequestWithToken { pub fn new( - target_id: TargetIdWithApid, + target_id: TargetId, request: Request, token: VerificationToken, ) -> Self { @@ -38,3 +45,50 @@ impl RequestWithToken { } } } + +#[derive(Default, Clone)] +pub struct GenericRequestRouter(pub HashMap>); + +impl PusHkRequestRouter for GenericRequestRouter { + type Error = GenericRoutingError; + + fn route( + &self, + target_id: TargetId, + hk_request: HkRequest, + token: VerificationToken, + ) -> Result<(), Self::Error> { + if let Some(sender) = self.0.get(&target_id) { + sender + .send(RequestWithToken::new( + target_id, + Request::Hk(hk_request), + token, + )) + .map_err(|_| GenericRoutingError::SendError(GenericSendError::RxDisconnected))?; + } + Ok(()) + } +} + +impl PusActionRequestRouter for GenericRequestRouter { + type Error = GenericRoutingError; + + fn route( + &self, + target_id: TargetId, + action_request: ActionRequest, + token: VerificationToken, + ) -> Result<(), Self::Error> { + if let Some(sender) = self.0.get(&target_id) { + sender + .send(RequestWithToken::new( + target_id, + Request::Action(action_request), + token, + )) + .map_err(|_| GenericRoutingError::SendError(GenericSendError::RxDisconnected))?; + } + Ok(()) + } +} diff --git a/satrs/src/action.rs b/satrs/src/action.rs index 0f74baa..43ba581 100644 --- a/satrs/src/action.rs +++ b/satrs/src/action.rs @@ -1,21 +1,38 @@ -use crate::{pool::StoreAddr, tmtc::TargetId}; +use crate::{pool::StoreAddr, TargetId}; pub type ActionId = u32; #[derive(Clone, Eq, PartialEq, Debug)] pub enum ActionRequest { - ActionIdAndStoreData((ActionId, StoreAddr)), - ActionIdAndVecData((ActionId, alloc::vec::Vec)), - StringIdAndVecData((alloc::string::String, alloc::vec::Vec)), - StringIdAndStoreData((alloc::string::String, StoreAddr)), + ActionIdAndStoreData { + action_id: ActionId, + data_addr: StoreAddr, + }, + ActionIdAndVecData { + action_id: ActionId, + data: alloc::vec::Vec, + }, + StringIdAndVecData { + action_id: alloc::string::String, + data: alloc::vec::Vec, + }, + StringIdAndStoreData { + action_id: alloc::string::String, + data: StoreAddr, + }, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct TargetedActionRequest { target: TargetId, - hk_request: ActionRequest, + action_request: ActionRequest, } -pub trait ActionRequestProvider { - fn route_action_request(&self, targeted_request: TargetedActionRequest); +impl TargetedActionRequest { + pub fn new(target: TargetId, action_request: ActionRequest) -> Self { + Self { + target, + action_request, + } + } } diff --git a/satrs/src/hk.rs b/satrs/src/hk.rs index ee6c31a..8033e15 100644 --- a/satrs/src/hk.rs +++ b/satrs/src/hk.rs @@ -1,4 +1,7 @@ -use crate::tmtc::TargetId; +use crate::{ + pus::verification::{TcStateAccepted, VerificationToken}, + TargetId, +}; pub type CollectionIntervalFactor = u32; pub type UniqueId = u32; @@ -13,10 +16,25 @@ pub enum HkRequest { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TargetedHkRequest { - target: TargetId, - hk_request: HkRequest, + pub target_id: TargetId, + pub hk_request: HkRequest, } -pub trait HkRequestProvider { - fn route_hk_request(&self, targeted_request: TargetedHkRequest); +impl TargetedHkRequest { + pub fn new(target_id: TargetId, hk_request: HkRequest) -> Self { + Self { + target_id, + hk_request, + } + } +} + +pub trait PusHkRequestRouter { + type Error; + fn route( + &self, + target_id: TargetId, + hk_request: HkRequest, + token: VerificationToken, + ) -> Result<(), Self::Error>; } diff --git a/satrs/src/lib.rs b/satrs/src/lib.rs index 15706f9..41ad938 100644 --- a/satrs/src/lib.rs +++ b/satrs/src/lib.rs @@ -43,6 +43,7 @@ pub mod params; pub mod pool; pub mod power; pub mod pus; +pub mod queue; pub mod request; pub mod res_code; pub mod seq_count; @@ -50,5 +51,8 @@ pub mod tmtc; pub use spacepackets; -// Generic channel ID type. +/// Generic channel ID type. pub type ChannelId = u32; + +/// Generic target ID type. +pub type TargetId = u64; diff --git a/satrs/src/mode.rs b/satrs/src/mode.rs index 7b26be4..c5968b4 100644 --- a/satrs/src/mode.rs +++ b/satrs/src/mode.rs @@ -1,9 +1,10 @@ -use crate::tmtc::TargetId; use core::mem::size_of; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use spacepackets::ByteConversionError; +use crate::TargetId; + #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ModeAndSubmode { diff --git a/satrs/src/objects.rs b/satrs/src/objects.rs index f16b567..a9b6881 100644 --- a/satrs/src/objects.rs +++ b/satrs/src/objects.rs @@ -51,7 +51,6 @@ //! assert_eq!(example_obj.id, obj_id); //! assert_eq!(example_obj.dummy, 42); //! ``` -use crate::tmtc::TargetId; #[cfg(feature = "alloc")] use alloc::boxed::Box; #[cfg(feature = "alloc")] @@ -63,6 +62,8 @@ use hashbrown::HashMap; #[cfg(feature = "std")] use std::error::Error; +use crate::TargetId; + #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub struct ObjectId { pub id: TargetId, diff --git a/satrs/src/pus/action.rs b/satrs/src/pus/action.rs new file mode 100644 index 0000000..88e146f --- /dev/null +++ b/satrs/src/pus/action.rs @@ -0,0 +1,115 @@ +use spacepackets::ecss::tc::PusTcReader; + +use crate::{action::ActionRequest, TargetId}; + +use super::verification::{TcStateAccepted, VerificationReporterWithSender, VerificationToken}; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub use std_mod::*; + +pub trait PusActionToRequestConverter { + type Error; + fn convert( + &mut self, + token: VerificationToken, + tc: &PusTcReader, + time_stamp: &[u8], + verif_reporter: &mut VerificationReporterWithSender, + ) -> Result<(TargetId, ActionRequest), Self::Error>; +} + +pub trait PusActionRequestRouter { + type Error; + fn route( + &self, + target_id: TargetId, + hk_request: ActionRequest, + token: VerificationToken, + ) -> Result<(), Self::Error>; +} + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub mod std_mod { + use crate::pus::{ + EcssTcInMemConverter, GenericRoutingError, PusPacketHandlerResult, PusPacketHandlingError, + PusRoutingErrorHandler, PusServiceBase, PusServiceHelper, + }; + + use super::*; + + pub struct PusService8ActionHandler< + TcInMemConverter: EcssTcInMemConverter, + RequestConverter: PusActionToRequestConverter, + RequestRouter: PusActionRequestRouter, + RoutingErrorHandler: PusRoutingErrorHandler, + > { + service_helper: PusServiceHelper, + request_converter: RequestConverter, + request_router: RequestRouter, + routing_error_handler: RoutingErrorHandler, + } + + impl< + TcInMemConverter: EcssTcInMemConverter, + RequestConverter: PusActionToRequestConverter, + RequestRouter: PusActionRequestRouter, + RoutingErrorHandler: PusRoutingErrorHandler, + > + PusService8ActionHandler< + TcInMemConverter, + RequestConverter, + RequestRouter, + RoutingErrorHandler, + > + { + pub fn new( + service_helper: PusServiceHelper, + request_converter: RequestConverter, + request_router: RequestRouter, + routing_error_handler: RoutingErrorHandler, + ) -> Self { + Self { + service_helper, + request_converter, + request_router, + routing_error_handler, + } + } + + pub fn handle_one_tc(&mut self) -> Result { + let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; + if possible_packet.is_none() { + return Ok(PusPacketHandlerResult::Empty); + } + let ecss_tc_and_token = possible_packet.unwrap(); + let tc = self + .service_helper + .tc_in_mem_converter + .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; + let mut partial_error = None; + let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error); + let (target_id, action_request) = self.request_converter.convert( + ecss_tc_and_token.token, + &tc, + &time_stamp, + &mut self.service_helper.common.verification_handler.borrow_mut(), + )?; + if let Err(e) = + self.request_router + .route(target_id, action_request, ecss_tc_and_token.token) + { + self.routing_error_handler.handle_error( + target_id, + ecss_tc_and_token.token, + &tc, + e, + &time_stamp, + &mut self.service_helper.common.verification_handler.borrow_mut(), + ); + } + Ok(PusPacketHandlerResult::RequestHandled) + } + } +} diff --git a/satrs/src/pus/hk.rs b/satrs/src/pus/hk.rs index 50dbc86..f4ed752 100644 --- a/satrs/src/pus/hk.rs +++ b/satrs/src/pus/hk.rs @@ -1 +1,110 @@ pub use spacepackets::ecss::hk::*; +use spacepackets::ecss::tc::PusTcReader; + +use crate::{hk::HkRequest, TargetId}; + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub use std_mod::*; + +use super::{ + verification::{TcStateAccepted, VerificationReporterWithSender, VerificationToken}, + EcssTcInMemConverter, GenericRoutingError, PusPacketHandlerResult, PusPacketHandlingError, + PusRoutingErrorHandler, PusServiceBase, PusServiceHelper, +}; + +pub trait PusHkToRequestConverter { + type Error; + fn convert( + &mut self, + token: VerificationToken, + tc: &PusTcReader, + time_stamp: &[u8], + verif_reporter: &mut VerificationReporterWithSender, + ) -> Result<(TargetId, HkRequest), Self::Error>; +} + +pub trait PusHkRequestRouter { + type Error; + fn route( + &self, + target_id: TargetId, + hk_request: HkRequest, + token: VerificationToken, + ) -> Result<(), Self::Error>; +} + +#[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] +pub mod std_mod { + use super::*; + + pub struct PusService3HkHandler< + TcInMemConverter: EcssTcInMemConverter, + RequestConverter: PusHkToRequestConverter, + RequestRouter: PusHkRequestRouter, + RoutingErrorHandler: PusRoutingErrorHandler, + > { + service_helper: PusServiceHelper, + request_converter: RequestConverter, + request_router: RequestRouter, + routing_error_handler: RoutingErrorHandler, + } + + impl< + TcInMemConverter: EcssTcInMemConverter, + RequestConverter: PusHkToRequestConverter, + RequestRouter: PusHkRequestRouter, + RoutingErrorHandler: PusRoutingErrorHandler, + > + PusService3HkHandler + { + pub fn new( + service_helper: PusServiceHelper, + request_converter: RequestConverter, + request_router: RequestRouter, + routing_error_handler: RoutingErrorHandler, + ) -> Self { + Self { + service_helper, + request_converter, + request_router, + routing_error_handler, + } + } + + pub fn handle_one_tc(&mut self) -> Result { + let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; + if possible_packet.is_none() { + return Ok(PusPacketHandlerResult::Empty); + } + let ecss_tc_and_token = possible_packet.unwrap(); + let tc = self + .service_helper + .tc_in_mem_converter + .convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?; + let mut partial_error = None; + let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error); + let (target_id, hk_request) = self.request_converter.convert( + ecss_tc_and_token.token, + &tc, + &time_stamp, + &mut self.service_helper.common.verification_handler.borrow_mut(), + )?; + if let Err(e) = + self.request_router + .route(target_id, hk_request, ecss_tc_and_token.token) + { + self.routing_error_handler.handle_error( + target_id, + ecss_tc_and_token.token, + &tc, + e, + &time_stamp, + &mut self.service_helper.common.verification_handler.borrow_mut(), + ); + } + Ok(PusPacketHandlerResult::RequestHandled) + } + } +} diff --git a/satrs/src/pus/mod.rs b/satrs/src/pus/mod.rs index 9001f8b..6b90747 100644 --- a/satrs/src/pus/mod.rs +++ b/satrs/src/pus/mod.rs @@ -2,7 +2,8 @@ //! //! This module contains structures to make working with the PUS C standard easier. //! The satrs-example application contains various usage examples of these components. -use crate::ChannelId; +use crate::queue::{GenericRecvError, GenericSendError}; +use crate::{ChannelId, TargetId}; use core::fmt::{Display, Formatter}; #[cfg(feature = "alloc")] use downcast_rs::{impl_downcast, Downcast}; @@ -16,6 +17,7 @@ use spacepackets::ecss::tm::PusTmCreator; use spacepackets::ecss::PusError; use spacepackets::{ByteConversionError, SpHeader}; +pub mod action; pub mod event; pub mod event_man; #[cfg(feature = "std")] @@ -37,6 +39,8 @@ use crate::pus::verification::{TcStateAccepted, TcStateToken, VerificationToken} #[cfg(feature = "std")] pub use std_mod::*; +use self::verification::VerificationReporterWithSender; + #[derive(Debug, PartialEq, Eq, Clone)] pub enum PusTmWrapper<'tm> { InStore(StoreAddr), @@ -55,52 +59,6 @@ impl<'tm> From> for PusTmWrapper<'tm> { } } -/// Generic error type for sending something via a message queue. -#[derive(Debug, Copy, Clone)] -pub enum GenericSendError { - RxDisconnected, - QueueFull(Option), -} - -impl Display for GenericSendError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - GenericSendError::RxDisconnected => { - write!(f, "rx side has disconnected") - } - GenericSendError::QueueFull(max_cap) => { - write!(f, "queue with max capacity of {max_cap:?} is full") - } - } - } -} - -#[cfg(feature = "std")] -impl Error for GenericSendError {} - -/// Generic error type for sending something via a message queue. -#[derive(Debug, Copy, Clone)] -pub enum GenericRecvError { - Empty, - TxDisconnected, -} - -impl Display for GenericRecvError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - Self::TxDisconnected => { - write!(f, "tx side has disconnected") - } - Self::Empty => { - write!(f, "nothing to receive") - } - } - } -} - -#[cfg(feature = "std")] -impl Error for GenericRecvError {} - #[derive(Debug, Clone)] pub enum EcssTmtcError { StoreLock, @@ -385,6 +343,19 @@ mod alloc_mod { impl EcssTcReceiver for T where T: EcssTcReceiverCore + 'static {} impl_downcast!(EcssTcReceiver); + + pub trait PusRoutingErrorHandler { + type Error; + fn handle_error( + &self, + target_id: TargetId, + token: VerificationToken, + tc: &PusTcReader, + error: GenericRoutingError, + time_stamp: &[u8], + verif_reporter: &mut VerificationReporterWithSender, + ); + } } #[cfg(feature = "std")] @@ -400,7 +371,7 @@ pub mod std_mod { TryRecvTmtcError, }; use crate::tmtc::tm_helper::SharedTmPool; - use crate::ChannelId; + use crate::{ChannelId, TargetId}; use alloc::boxed::Box; use alloc::vec::Vec; use crossbeam_channel as cb; @@ -662,6 +633,20 @@ pub mod std_mod { } } + // TODO: All these types could probably be no_std if we implemented error handling ourselves.. + // but thiserror is really nice, so keep it like this for simplicity for now. Maybe thiserror + // will be no_std soon, see https://github.com/rust-lang/rust/issues/103765 . + + #[derive(Debug, Clone, Error)] + pub enum GenericRoutingError { + #[error("Not enough application data, expected at least 4 bytes for target ID")] + NotEnoughAppData, + #[error("Unknown target ID {0}")] + UnknownTargetId(TargetId), + #[error("Sending action request failed: {0}")] + SendError(GenericSendError), + } + #[derive(Debug, Clone, Error)] pub enum PusPacketHandlingError { #[error("generic PUS error: {0}")] @@ -682,6 +667,8 @@ pub mod std_mod { EcssTmtc(#[from] EcssTmtcError), #[error("invalid verification token")] InvalidVerificationToken, + #[error("request routing error: {0}")] + RequestRoutingError(#[from] GenericRoutingError), #[error("other error {0}")] Other(String), } diff --git a/satrs/src/queue.rs b/satrs/src/queue.rs new file mode 100644 index 0000000..25ff6c6 --- /dev/null +++ b/satrs/src/queue.rs @@ -0,0 +1,49 @@ +use core::fmt::{Display, Formatter}; +#[cfg(feature = "std")] +use std::error::Error; + +/// Generic error type for sending something via a message queue. +#[derive(Debug, Copy, Clone)] +pub enum GenericSendError { + RxDisconnected, + QueueFull(Option), +} + +impl Display for GenericSendError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + GenericSendError::RxDisconnected => { + write!(f, "rx side has disconnected") + } + GenericSendError::QueueFull(max_cap) => { + write!(f, "queue with max capacity of {max_cap:?} is full") + } + } + } +} + +#[cfg(feature = "std")] +impl Error for GenericSendError {} + +/// Generic error type for sending something via a message queue. +#[derive(Debug, Copy, Clone)] +pub enum GenericRecvError { + Empty, + TxDisconnected, +} + +impl Display for GenericRecvError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Self::TxDisconnected => { + write!(f, "tx side has disconnected") + } + Self::Empty => { + write!(f, "nothing to receive") + } + } + } +} + +#[cfg(feature = "std")] +impl Error for GenericRecvError {} diff --git a/satrs/src/request.rs b/satrs/src/request.rs index 2a9a825..ee60b21 100644 --- a/satrs/src/request.rs +++ b/satrs/src/request.rs @@ -1,3 +1,85 @@ -pub trait RequestRouter { - //fn route_request(&self, request: Request); +use core::fmt; + +use spacepackets::{ + ecss::{tc::IsPusTelecommand, PusPacket}, + ByteConversionError, CcsdsPacket, +}; + +use thiserror::Error; + +use crate::TargetId; + +pub type Apid = u16; + +#[derive(Debug, Error)] +pub enum TargetIdCreationError { + #[error("byte conversion")] + ByteConversion(#[from] ByteConversionError), + #[error("not enough app data to generate target ID")] + NotEnoughAppData(usize), +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TargetAndApidId { + pub apid: Apid, + pub target: u32, +} + +impl TargetAndApidId { + pub fn new(apid: Apid, target: u32) -> Self { + Self { apid, target } + } + + pub fn apid(&self) -> Apid { + self.apid + } + + pub fn target(&self) -> u32 { + self.target + } + + pub fn raw(&self) -> TargetId { + ((self.apid as u64) << 32) | (self.target as u64) + } + + pub fn target_id(&self) -> TargetId { + self.raw() + } + + pub fn from_pus_tc( + tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand), + ) -> Result { + if tc.user_data().len() < 4 { + return Err(ByteConversionError::FromSliceTooSmall { + found: tc.user_data().len(), + expected: 8, + } + .into()); + } + Ok(Self { + apid: tc.apid(), + target: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()), + }) + } +} + +impl From for TargetAndApidId { + fn from(raw: u64) -> Self { + Self { + apid: (raw >> 32) as u16, + target: raw as u32, + } + } +} + +impl From for u64 { + fn from(target_and_apid_id: TargetAndApidId) -> Self { + target_and_apid_id.raw() + } +} + +impl fmt::Display for TargetAndApidId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}, {}", self.apid, self.target) + } } diff --git a/satrs/src/tmtc/mod.rs b/satrs/src/tmtc/mod.rs index 0f548a2..b930359 100644 --- a/satrs/src/tmtc/mod.rs +++ b/satrs/src/tmtc/mod.rs @@ -20,8 +20,6 @@ pub use ccsds_distrib::{CcsdsDistributor, CcsdsError, CcsdsPacketHandler}; #[cfg(feature = "alloc")] pub use pus_distrib::{PusDistributor, PusServiceProvider}; -pub type TargetId = u32; - /// Generic trait for object which can receive any telecommands in form of a raw bytestream, with /// no assumptions about the received protocol. ///