First PUS handler abstraction with request mapping
Some checks are pending
Rust/sat-rs/pipeline/head Build started...
Some checks are pending
Rust/sat-rs/pipeline/head Build started...
This is the first attempt of a generic PUS service abstraction where the PUS telecommands need to be converted into targetted requests.
This commit is contained in:
parent
e426a7cef1
commit
a5e5a1a5a0
@ -3,6 +3,7 @@ use std::sync::mpsc::{self, TryRecvError};
|
|||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use satrs::pus::verification::VerificationReporterWithSender;
|
use satrs::pus::verification::VerificationReporterWithSender;
|
||||||
use satrs::pus::{EcssTmSender, PusTmWrapper};
|
use satrs::pus::{EcssTmSender, PusTmWrapper};
|
||||||
|
use satrs::request::TargetAndApidId;
|
||||||
use satrs::spacepackets::ecss::hk::Subservice as HkSubservice;
|
use satrs::spacepackets::ecss::hk::Subservice as HkSubservice;
|
||||||
use satrs::{
|
use satrs::{
|
||||||
hk::HkRequest,
|
hk::HkRequest,
|
||||||
@ -70,12 +71,12 @@ impl AcsTask {
|
|||||||
"ACS thread: Received HK request {:?}",
|
"ACS thread: Received HK request {:?}",
|
||||||
request.targeted_request
|
request.targeted_request
|
||||||
);
|
);
|
||||||
|
let target_and_apid_id = TargetAndApidId::from(request.targeted_request.target_id);
|
||||||
match request.targeted_request.request {
|
match request.targeted_request.request {
|
||||||
Request::Hk(hk_req) => match hk_req {
|
Request::Hk(hk_req) => match hk_req {
|
||||||
HkRequest::OneShot(unique_id) => self.handle_hk_request(
|
HkRequest::OneShot(unique_id) => {
|
||||||
request.targeted_request.target_id_with_apid.target_id(),
|
self.handle_hk_request(target_and_apid_id.target(), unique_id)
|
||||||
unique_id,
|
}
|
||||||
),
|
|
||||||
HkRequest::Enable(_) => {}
|
HkRequest::Enable(_) => {}
|
||||||
HkRequest::Disable(_) => {}
|
HkRequest::Disable(_) => {}
|
||||||
HkRequest::ModifyCollectionInterval(_, _) => {}
|
HkRequest::ModifyCollectionInterval(_, _) => {}
|
||||||
|
@ -48,7 +48,11 @@ pub mod tmtc_err {
|
|||||||
#[resultcode]
|
#[resultcode]
|
||||||
pub const PUS_SERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 2);
|
pub const PUS_SERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 2);
|
||||||
#[resultcode]
|
#[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(
|
#[resultcode(
|
||||||
info = "Not enough data inside the TC application data field. Optionally includes: \
|
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] = &[
|
pub const TMTC_RESULTS: &[ResultU16Info] = &[
|
||||||
INVALID_PUS_SERVICE_EXT,
|
INVALID_PUS_SERVICE_EXT,
|
||||||
INVALID_PUS_SUBSERVICE_EXT,
|
INVALID_PUS_SUBSERVICE_EXT,
|
||||||
|
PUS_SERVICE_NOT_IMPLEMENTED_EXT,
|
||||||
|
UNKNOWN_TARGET_ID_EXT,
|
||||||
|
ROUTING_ERROR_EXT,
|
||||||
NOT_ENOUGH_APP_DATA_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);
|
pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Hk as u8, 2);
|
||||||
#[resultcode]
|
#[resultcode]
|
||||||
pub const COLLECTION_INTERVAL_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 3);
|
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)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
@ -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 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<Self, TargetIdCreationError> {
|
|
||||||
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()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -17,6 +17,7 @@ use log::info;
|
|||||||
use pus::test::create_test_service_dynamic;
|
use pus::test::create_test_service_dynamic;
|
||||||
use satrs::hal::std::tcp_server::ServerConfig;
|
use satrs::hal::std::tcp_server::ServerConfig;
|
||||||
use satrs::hal::std::udp_server::UdpTcServer;
|
use satrs::hal::std::udp_server::UdpTcServer;
|
||||||
|
use satrs::request::TargetAndApidId;
|
||||||
use satrs::tmtc::tm_helper::SharedTmPool;
|
use satrs::tmtc::tm_helper::SharedTmPool;
|
||||||
use satrs_example::config::pool::{create_sched_tc_pool, create_static_pools};
|
use satrs_example::config::pool::{create_sched_tc_pool, create_static_pools};
|
||||||
use satrs_example::config::tasks::{
|
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::scheduler::{create_scheduler_service_dynamic, create_scheduler_service_static};
|
||||||
use crate::pus::test::create_test_service_static;
|
use crate::pus::test::create_test_service_static;
|
||||||
use crate::pus::{PusReceiver, PusTcMpscRouter};
|
use crate::pus::{PusReceiver, PusTcMpscRouter};
|
||||||
use crate::requests::RequestWithToken;
|
use crate::requests::{GenericRequestRouter, RequestWithToken};
|
||||||
use crate::tcp::{SyncTcpTmSource, TcpTask};
|
use crate::tcp::{SyncTcpTmSource, TcpTask};
|
||||||
use crate::tmtc::{
|
use crate::tmtc::{
|
||||||
PusTcSourceProviderSharedPool, SharedTcPool, TcSourceTaskDynamic, TcSourceTaskStatic,
|
PusTcSourceProviderSharedPool, SharedTcPool, TcSourceTaskDynamic, TcSourceTaskStatic,
|
||||||
@ -45,10 +46,8 @@ use satrs::pus::event_man::EventRequestWithToken;
|
|||||||
use satrs::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender};
|
use satrs::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender};
|
||||||
use satrs::pus::{EcssTmSender, MpscTmAsVecSender, MpscTmInSharedPoolSender};
|
use satrs::pus::{EcssTmSender, MpscTmAsVecSender, MpscTmInSharedPoolSender};
|
||||||
use satrs::spacepackets::{time::cds::TimeProvider, time::TimeWriter};
|
use satrs::spacepackets::{time::cds::TimeProvider, time::TimeWriter};
|
||||||
use satrs::tmtc::{CcsdsDistributor, TargetId};
|
use satrs::tmtc::CcsdsDistributor;
|
||||||
use satrs::ChannelId;
|
use satrs::ChannelId;
|
||||||
use satrs_example::TargetIdWithApid;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::sync::mpsc::{self, channel};
|
use std::sync::mpsc::{self, channel};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
@ -82,11 +81,11 @@ fn static_tmtc_pool_main() {
|
|||||||
tm_funnel_tx.clone(),
|
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::<RequestWithToken>();
|
let (acs_thread_tx, acs_thread_rx) = channel::<RequestWithToken>();
|
||||||
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
||||||
let mut request_map = HashMap::new();
|
let mut request_map = GenericRequestRouter::default();
|
||||||
request_map.insert(acs_target_id, acs_thread_tx);
|
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
|
// This helper structure is used by all telecommand providers which need to send telecommands
|
||||||
// to the TC source.
|
// to the TC source.
|
||||||
@ -310,11 +309,11 @@ fn dyn_tmtc_pool_main() {
|
|||||||
tm_funnel_tx.clone(),
|
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::<RequestWithToken>();
|
let (acs_thread_tx, acs_thread_rx) = channel::<RequestWithToken>();
|
||||||
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
|
||||||
let mut request_map = HashMap::new();
|
let mut request_map = GenericRequestRouter::default();
|
||||||
request_map.insert(acs_target_id, acs_thread_tx);
|
request_map.0.insert(acs_target_id.into(), acs_thread_tx);
|
||||||
|
|
||||||
let tc_source = PusTcSourceProviderDynamic(tc_source_tx);
|
let tc_source = PusTcSourceProviderDynamic(tc_source_tx);
|
||||||
|
|
||||||
|
@ -1,23 +1,74 @@
|
|||||||
use crate::requests::{Request, RequestWithToken};
|
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use satrs::action::ActionRequest;
|
use satrs::action::ActionRequest;
|
||||||
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
|
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
|
||||||
|
use satrs::pus::action::{PusActionToRequestConverter, PusService8ActionHandler};
|
||||||
use satrs::pus::verification::{
|
use satrs::pus::verification::{
|
||||||
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationToken,
|
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
|
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
|
||||||
EcssTcReceiver, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender,
|
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
|
||||||
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper,
|
PusPacketHandlingError, PusServiceHelper,
|
||||||
};
|
};
|
||||||
|
use satrs::request::TargetAndApidId;
|
||||||
use satrs::spacepackets::ecss::tc::PusTcReader;
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::PusPacket;
|
use satrs::spacepackets::ecss::PusPacket;
|
||||||
use satrs::tmtc::tm_helper::SharedTmPool;
|
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::config::{tmtc_err, TcReceiverId, TmSenderId, PUS_APID};
|
||||||
use satrs_example::TargetIdWithApid;
|
use std::sync::mpsc::{self};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::mpsc::{self, Sender};
|
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<TcStateAccepted>,
|
||||||
|
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(
|
pub fn create_action_service_static(
|
||||||
shared_tm_store: SharedTmPool,
|
shared_tm_store: SharedTmPool,
|
||||||
@ -25,7 +76,7 @@ pub fn create_action_service_static(
|
|||||||
verif_reporter: VerificationReporterWithSender,
|
verif_reporter: VerificationReporterWithSender,
|
||||||
tc_pool: SharedStaticMemoryPool,
|
tc_pool: SharedStaticMemoryPool,
|
||||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
|
action_router: GenericRequestRouter,
|
||||||
) -> Pus8Wrapper<EcssTcInSharedStoreConverter> {
|
) -> Pus8Wrapper<EcssTcInSharedStoreConverter> {
|
||||||
let action_srv_tm_sender = MpscTmInSharedPoolSender::new(
|
let action_srv_tm_sender = MpscTmInSharedPoolSender::new(
|
||||||
TmSenderId::PusAction as ChannelId,
|
TmSenderId::PusAction as ChannelId,
|
||||||
@ -39,12 +90,16 @@ pub fn create_action_service_static(
|
|||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
);
|
);
|
||||||
let pus_8_handler = PusService8ActionHandler::new(
|
let pus_8_handler = PusService8ActionHandler::new(
|
||||||
Box::new(action_srv_receiver),
|
PusServiceHelper::new(
|
||||||
Box::new(action_srv_tm_sender),
|
Box::new(action_srv_receiver),
|
||||||
PUS_APID,
|
Box::new(action_srv_tm_sender),
|
||||||
verif_reporter.clone(),
|
PUS_APID,
|
||||||
EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
|
verif_reporter.clone(),
|
||||||
request_map.clone(),
|
EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
|
||||||
|
),
|
||||||
|
ExampleActionRequestConverter::default(),
|
||||||
|
action_router,
|
||||||
|
GenericRoutingErrorHandler::<8>::default(),
|
||||||
);
|
);
|
||||||
Pus8Wrapper { pus_8_handler }
|
Pus8Wrapper { pus_8_handler }
|
||||||
}
|
}
|
||||||
@ -53,7 +108,7 @@ pub fn create_action_service_dynamic(
|
|||||||
tm_funnel_tx: mpsc::Sender<Vec<u8>>,
|
tm_funnel_tx: mpsc::Sender<Vec<u8>>,
|
||||||
verif_reporter: VerificationReporterWithSender,
|
verif_reporter: VerificationReporterWithSender,
|
||||||
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
|
action_router: GenericRequestRouter,
|
||||||
) -> Pus8Wrapper<EcssTcInVecConverter> {
|
) -> Pus8Wrapper<EcssTcInVecConverter> {
|
||||||
let action_srv_tm_sender = MpscTmAsVecSender::new(
|
let action_srv_tm_sender = MpscTmAsVecSender::new(
|
||||||
TmSenderId::PusAction as ChannelId,
|
TmSenderId::PusAction as ChannelId,
|
||||||
@ -66,146 +121,27 @@ pub fn create_action_service_dynamic(
|
|||||||
pus_action_rx,
|
pus_action_rx,
|
||||||
);
|
);
|
||||||
let pus_8_handler = PusService8ActionHandler::new(
|
let pus_8_handler = PusService8ActionHandler::new(
|
||||||
Box::new(action_srv_receiver),
|
PusServiceHelper::new(
|
||||||
Box::new(action_srv_tm_sender),
|
Box::new(action_srv_receiver),
|
||||||
PUS_APID,
|
Box::new(action_srv_tm_sender),
|
||||||
verif_reporter.clone(),
|
PUS_APID,
|
||||||
EcssTcInVecConverter::default(),
|
verif_reporter.clone(),
|
||||||
request_map.clone(),
|
EcssTcInVecConverter::default(),
|
||||||
|
),
|
||||||
|
ExampleActionRequestConverter::default(),
|
||||||
|
action_router,
|
||||||
|
GenericRoutingErrorHandler::<8>::default(),
|
||||||
);
|
);
|
||||||
Pus8Wrapper { pus_8_handler }
|
Pus8Wrapper { pus_8_handler }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PusService8ActionHandler<TcInMemConverter: EcssTcInMemConverter> {
|
|
||||||
service_helper: PusServiceHelper<TcInMemConverter>,
|
|
||||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemConverter> {
|
|
||||||
pub fn new(
|
|
||||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
|
||||||
tm_sender: Box<dyn EcssTmSender>,
|
|
||||||
tm_apid: u16,
|
|
||||||
verification_handler: VerificationReporterWithSender,
|
|
||||||
tc_in_mem_converter: TcInMemConverter,
|
|
||||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
|
||||||
) -> 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<TcStateAccepted>,
|
|
||||||
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<PusPacketHandlerResult, PusPacketHandlingError> {
|
|
||||||
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<TcInMemConverter: EcssTcInMemConverter> {
|
pub struct Pus8Wrapper<TcInMemConverter: EcssTcInMemConverter> {
|
||||||
pub(crate) pus_8_handler: PusService8ActionHandler<TcInMemConverter>,
|
pub(crate) pus_8_handler: PusService8ActionHandler<
|
||||||
|
TcInMemConverter,
|
||||||
|
ExampleActionRequestConverter,
|
||||||
|
GenericRequestRouter,
|
||||||
|
GenericRoutingErrorHandler<8>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TcInMemConverter: EcssTcInMemConverter> Pus8Wrapper<TcInMemConverter> {
|
impl<TcInMemConverter: EcssTcInMemConverter> Pus8Wrapper<TcInMemConverter> {
|
||||||
|
@ -1,122 +1,46 @@
|
|||||||
use crate::requests::{Request, RequestWithToken};
|
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use satrs::hk::{CollectionIntervalFactor, HkRequest};
|
use satrs::hk::{CollectionIntervalFactor, HkRequest};
|
||||||
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
|
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
|
||||||
|
use satrs::pus::hk::{PusHkToRequestConverter, PusService3HkHandler};
|
||||||
use satrs::pus::verification::{
|
use satrs::pus::verification::{
|
||||||
FailParams, StdVerifReporterWithSender, VerificationReporterWithSender,
|
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationToken,
|
||||||
};
|
};
|
||||||
use satrs::pus::{
|
use satrs::pus::{
|
||||||
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
|
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
|
||||||
EcssTcReceiver, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender,
|
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
|
||||||
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper,
|
PusPacketHandlingError, PusServiceHelper,
|
||||||
};
|
};
|
||||||
|
use satrs::request::TargetAndApidId;
|
||||||
|
use satrs::spacepackets::ecss::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::{hk, PusPacket};
|
use satrs::spacepackets::ecss::{hk, PusPacket};
|
||||||
use satrs::tmtc::tm_helper::SharedTmPool;
|
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::config::{hk_err, tmtc_err, TcReceiverId, TmSenderId, PUS_APID};
|
||||||
use satrs_example::TargetIdWithApid;
|
use std::sync::mpsc::{self};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::mpsc::{self, Sender};
|
|
||||||
|
|
||||||
pub fn create_hk_service_static(
|
use crate::requests::GenericRequestRouter;
|
||||||
shared_tm_store: SharedTmPool,
|
|
||||||
tm_funnel_tx: mpsc::Sender<StoreAddr>,
|
|
||||||
verif_reporter: VerificationReporterWithSender,
|
|
||||||
tc_pool: SharedStaticMemoryPool,
|
|
||||||
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
|
||||||
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
|
|
||||||
) -> Pus3Wrapper<EcssTcInSharedStoreConverter> {
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_hk_service_dynamic(
|
use super::GenericRoutingErrorHandler;
|
||||||
tm_funnel_tx: mpsc::Sender<Vec<u8>>,
|
|
||||||
verif_reporter: VerificationReporterWithSender,
|
|
||||||
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
|
||||||
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
|
|
||||||
) -> Pus3Wrapper<EcssTcInVecConverter> {
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PusService3HkHandler<TcInMemConverter: EcssTcInMemConverter> {
|
#[derive(Default)]
|
||||||
psb: PusServiceHelper<TcInMemConverter>,
|
pub struct ExampleHkRequestConverter {}
|
||||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService3HkHandler<TcInMemConverter> {
|
impl PusHkToRequestConverter for ExampleHkRequestConverter {
|
||||||
pub fn new(
|
type Error = PusPacketHandlingError;
|
||||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
|
||||||
tm_sender: Box<dyn EcssTmSender>,
|
|
||||||
tm_apid: u16,
|
|
||||||
verification_handler: StdVerifReporterWithSender,
|
|
||||||
tc_in_mem_converter: TcInMemConverter,
|
|
||||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
psb: PusServiceHelper::new(
|
|
||||||
tc_receiver,
|
|
||||||
tm_sender,
|
|
||||||
tm_apid,
|
|
||||||
verification_handler,
|
|
||||||
tc_in_mem_converter,
|
|
||||||
),
|
|
||||||
request_handlers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
|
fn convert(
|
||||||
let possible_packet = self.psb.retrieve_and_accept_next_packet()?;
|
&mut self,
|
||||||
if possible_packet.is_none() {
|
token: VerificationToken<TcStateAccepted>,
|
||||||
return Ok(PusPacketHandlerResult::Empty);
|
tc: &PusTcReader,
|
||||||
}
|
time_stamp: &[u8],
|
||||||
let ecss_tc_and_token = possible_packet.unwrap();
|
verif_reporter: &mut VerificationReporterWithSender,
|
||||||
let tc = self
|
) -> Result<(TargetId, HkRequest), Self::Error> {
|
||||||
.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);
|
|
||||||
let user_data = tc.user_data();
|
let user_data = tc.user_data();
|
||||||
if user_data.is_empty() {
|
if user_data.is_empty() {
|
||||||
self.psb
|
verif_reporter
|
||||||
.common
|
|
||||||
.verification_handler
|
|
||||||
.borrow_mut()
|
|
||||||
.start_failure(
|
.start_failure(
|
||||||
ecss_tc_and_token.token,
|
token,
|
||||||
FailParams::new(Some(&time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None),
|
FailParams::new(Some(time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None),
|
||||||
)
|
)
|
||||||
.expect("Sending start failure TM failed");
|
.expect("Sending start failure TM failed");
|
||||||
return Err(PusPacketHandlingError::NotEnoughAppData(
|
return Err(PusPacketHandlingError::NotEnoughAppData(
|
||||||
@ -129,84 +53,153 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService3HkHandler<TcInMemConvert
|
|||||||
} else {
|
} else {
|
||||||
&hk_err::UNIQUE_ID_MISSING
|
&hk_err::UNIQUE_ID_MISSING
|
||||||
};
|
};
|
||||||
self.psb
|
verif_reporter
|
||||||
.common
|
.start_failure(token, FailParams::new(Some(time_stamp), err, None))
|
||||||
.verification_handler
|
|
||||||
.borrow_mut()
|
|
||||||
.start_failure(
|
|
||||||
ecss_tc_and_token.token,
|
|
||||||
FailParams::new(Some(&time_stamp), err, None),
|
|
||||||
)
|
|
||||||
.expect("Sending start failure TM failed");
|
.expect("Sending start failure TM failed");
|
||||||
return Err(PusPacketHandlingError::NotEnoughAppData(
|
return Err(PusPacketHandlingError::NotEnoughAppData(
|
||||||
"Expected at least 8 bytes of app data".into(),
|
"Expected at least 8 bytes of app data".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let target_id = TargetIdWithApid::from_tc(&tc).expect("invalid tc format");
|
let subservice = tc.subservice();
|
||||||
let unique_id = u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap());
|
let target_id = TargetAndApidId::from_pus_tc(tc).expect("invalid tc format");
|
||||||
if !self.request_handlers.contains_key(&target_id) {
|
let unique_id = u32::from_be_bytes(tc.user_data()[4..8].try_into().unwrap());
|
||||||
self.psb
|
|
||||||
.common
|
let standard_subservice = hk::Subservice::try_from(subservice);
|
||||||
.verification_handler
|
if standard_subservice.is_err() {
|
||||||
.borrow_mut()
|
verif_reporter
|
||||||
.start_failure(
|
.start_failure(
|
||||||
ecss_tc_and_token.token,
|
token,
|
||||||
FailParams::new(Some(&time_stamp), &hk_err::UNKNOWN_TARGET_ID, None),
|
FailParams::new(
|
||||||
|
Some(time_stamp),
|
||||||
|
&tmtc_err::INVALID_PUS_SUBSERVICE,
|
||||||
|
Some(&[subservice]),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.expect("Sending start failure TM failed");
|
.expect("Sending start failure TM failed");
|
||||||
return Err(PusPacketHandlingError::NotEnoughAppData(format!(
|
return Err(PusPacketHandlingError::InvalidSubservice(subservice));
|
||||||
"Unknown target ID {target_id}"
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
let send_request = |target: TargetIdWithApid, request: HkRequest| {
|
Ok((
|
||||||
let sender = self.request_handlers.get(&target).unwrap();
|
target_id.into(),
|
||||||
sender
|
match standard_subservice.unwrap() {
|
||||||
.send(RequestWithToken::new(
|
hk::Subservice::TcEnableHkGeneration | hk::Subservice::TcEnableDiagGeneration => {
|
||||||
target,
|
HkRequest::Enable(unique_id)
|
||||||
Request::Hk(request),
|
}
|
||||||
ecss_tc_and_token.token,
|
hk::Subservice::TcDisableHkGeneration | hk::Subservice::TcDisableDiagGeneration => {
|
||||||
))
|
HkRequest::Disable(unique_id)
|
||||||
.unwrap_or_else(|_| panic!("Sending HK request {request:?} failed"));
|
}
|
||||||
};
|
hk::Subservice::TcReportHkReportStructures => todo!(),
|
||||||
if subservice == hk::Subservice::TcEnableHkGeneration as u8 {
|
hk::Subservice::TmHkPacket => todo!(),
|
||||||
send_request(target_id, HkRequest::Enable(unique_id));
|
hk::Subservice::TcGenerateOneShotHk | hk::Subservice::TcGenerateOneShotDiag => {
|
||||||
} else if subservice == hk::Subservice::TcDisableHkGeneration as u8 {
|
HkRequest::OneShot(unique_id)
|
||||||
send_request(target_id, HkRequest::Disable(unique_id));
|
}
|
||||||
} else if subservice == hk::Subservice::TcGenerateOneShotHk as u8 {
|
hk::Subservice::TcModifyDiagCollectionInterval
|
||||||
send_request(target_id, HkRequest::OneShot(unique_id));
|
| hk::Subservice::TcModifyHkCollectionInterval => {
|
||||||
} else if subservice == hk::Subservice::TcModifyHkCollectionInterval as u8 {
|
if user_data.len() < 12 {
|
||||||
if user_data.len() < 12 {
|
verif_reporter
|
||||||
self.psb
|
.start_failure(
|
||||||
.common
|
token,
|
||||||
.verification_handler
|
FailParams::new(
|
||||||
.borrow_mut()
|
Some(time_stamp),
|
||||||
.start_failure(
|
&tmtc_err::NOT_ENOUGH_APP_DATA,
|
||||||
ecss_tc_and_token.token,
|
None,
|
||||||
FailParams::new(
|
),
|
||||||
Some(&time_stamp),
|
)
|
||||||
&hk_err::COLLECTION_INTERVAL_MISSING,
|
.expect("Sending start failure TM failed");
|
||||||
None,
|
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(),
|
verif_reporter
|
||||||
));
|
.start_failure(
|
||||||
}
|
token,
|
||||||
send_request(
|
FailParams::new(
|
||||||
target_id,
|
Some(time_stamp),
|
||||||
HkRequest::ModifyCollectionInterval(
|
&tmtc_err::PUS_SUBSERVICE_NOT_IMPLEMENTED,
|
||||||
unique_id,
|
Some(&[subservice]),
|
||||||
CollectionIntervalFactor::from_be_bytes(user_data[8..12].try_into().unwrap()),
|
),
|
||||||
),
|
)
|
||||||
);
|
.expect("Sending start failure TM failed");
|
||||||
}
|
return Err(PusPacketHandlingError::InvalidSubservice(subservice));
|
||||||
Ok(PusPacketHandlerResult::RequestHandled)
|
}
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_hk_service_static(
|
||||||
|
shared_tm_store: SharedTmPool,
|
||||||
|
tm_funnel_tx: mpsc::Sender<StoreAddr>,
|
||||||
|
verif_reporter: VerificationReporterWithSender,
|
||||||
|
tc_pool: SharedStaticMemoryPool,
|
||||||
|
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
|
request_router: GenericRequestRouter,
|
||||||
|
) -> Pus3Wrapper<EcssTcInSharedStoreConverter> {
|
||||||
|
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<Vec<u8>>,
|
||||||
|
verif_reporter: VerificationReporterWithSender,
|
||||||
|
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
|
||||||
|
request_router: GenericRequestRouter,
|
||||||
|
) -> Pus3Wrapper<EcssTcInVecConverter> {
|
||||||
|
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<TcInMemConverter: EcssTcInMemConverter> {
|
pub struct Pus3Wrapper<TcInMemConverter: EcssTcInMemConverter> {
|
||||||
pub(crate) pus_3_handler: PusService3HkHandler<TcInMemConverter>,
|
pub(crate) pus_3_handler: PusService3HkHandler<
|
||||||
|
TcInMemConverter,
|
||||||
|
ExampleHkRequestConverter,
|
||||||
|
GenericRequestRouter,
|
||||||
|
GenericRoutingErrorHandler<3>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TcInMemConverter: EcssTcInMemConverter> Pus3Wrapper<TcInMemConverter> {
|
impl<TcInMemConverter: EcssTcInMemConverter> Pus3Wrapper<TcInMemConverter> {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::tmtc::MpscStoreAndSendError;
|
use crate::tmtc::MpscStoreAndSendError;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use satrs::pus::verification::{FailParams, StdVerifReporterWithSender};
|
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::tc::PusTcReader;
|
||||||
use satrs::spacepackets::ecss::PusServiceId;
|
use satrs::spacepackets::ecss::PusServiceId;
|
||||||
use satrs::spacepackets::time::cds::TimeProvider;
|
use satrs::spacepackets::time::cds::TimeProvider;
|
||||||
@ -151,3 +153,62 @@ impl PusReceiver {
|
|||||||
Ok(PusPacketHandlerResult::RequestHandled)
|
Ok(PusPacketHandlerResult::RequestHandled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct GenericRoutingErrorHandler<const SERVICE_ID: u8> {}
|
||||||
|
|
||||||
|
impl<const SERVICE_ID: u8> PusRoutingErrorHandler for GenericRoutingErrorHandler<SERVICE_ID> {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
45
satrs-example/src/queue.rs
Normal file
45
satrs-example/src/queue.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/// Generic error type for sending something via a message queue.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum GenericSendError {
|
||||||
|
RxDisconnected,
|
||||||
|
QueueFull(Option<u32>),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {}
|
@ -1,9 +1,16 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use satrs::action::ActionRequest;
|
use satrs::action::ActionRequest;
|
||||||
use satrs::hk::HkRequest;
|
use satrs::hk::HkRequest;
|
||||||
use satrs::mode::ModeRequest;
|
use satrs::mode::ModeRequest;
|
||||||
|
use satrs::pus::action::PusActionRequestRouter;
|
||||||
|
use satrs::pus::hk::PusHkRequestRouter;
|
||||||
use satrs::pus::verification::{TcStateAccepted, VerificationToken};
|
use satrs::pus::verification::{TcStateAccepted, VerificationToken};
|
||||||
use satrs_example::TargetIdWithApid;
|
use satrs::pus::GenericRoutingError;
|
||||||
|
use satrs::queue::GenericSendError;
|
||||||
|
use satrs::TargetId;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
@ -16,7 +23,7 @@ pub enum Request {
|
|||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug, new)]
|
#[derive(Clone, Eq, PartialEq, Debug, new)]
|
||||||
pub struct TargetedRequest {
|
pub struct TargetedRequest {
|
||||||
pub(crate) target_id_with_apid: TargetIdWithApid,
|
pub(crate) target_id: TargetId,
|
||||||
pub(crate) request: Request,
|
pub(crate) request: Request,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +35,7 @@ pub struct RequestWithToken {
|
|||||||
|
|
||||||
impl RequestWithToken {
|
impl RequestWithToken {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
target_id: TargetIdWithApid,
|
target_id: TargetId,
|
||||||
request: Request,
|
request: Request,
|
||||||
token: VerificationToken<TcStateAccepted>,
|
token: VerificationToken<TcStateAccepted>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -38,3 +45,50 @@ impl RequestWithToken {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct GenericRequestRouter(pub HashMap<TargetId, mpsc::Sender<RequestWithToken>>);
|
||||||
|
|
||||||
|
impl PusHkRequestRouter for GenericRequestRouter {
|
||||||
|
type Error = GenericRoutingError;
|
||||||
|
|
||||||
|
fn route(
|
||||||
|
&self,
|
||||||
|
target_id: TargetId,
|
||||||
|
hk_request: HkRequest,
|
||||||
|
token: VerificationToken<TcStateAccepted>,
|
||||||
|
) -> 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<TcStateAccepted>,
|
||||||
|
) -> 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,21 +1,38 @@
|
|||||||
use crate::{pool::StoreAddr, tmtc::TargetId};
|
use crate::{pool::StoreAddr, TargetId};
|
||||||
|
|
||||||
pub type ActionId = u32;
|
pub type ActionId = u32;
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
pub enum ActionRequest {
|
pub enum ActionRequest {
|
||||||
ActionIdAndStoreData((ActionId, StoreAddr)),
|
ActionIdAndStoreData {
|
||||||
ActionIdAndVecData((ActionId, alloc::vec::Vec<u8>)),
|
action_id: ActionId,
|
||||||
StringIdAndVecData((alloc::string::String, alloc::vec::Vec<u8>)),
|
data_addr: StoreAddr,
|
||||||
StringIdAndStoreData((alloc::string::String, StoreAddr)),
|
},
|
||||||
|
ActionIdAndVecData {
|
||||||
|
action_id: ActionId,
|
||||||
|
data: alloc::vec::Vec<u8>,
|
||||||
|
},
|
||||||
|
StringIdAndVecData {
|
||||||
|
action_id: alloc::string::String,
|
||||||
|
data: alloc::vec::Vec<u8>,
|
||||||
|
},
|
||||||
|
StringIdAndStoreData {
|
||||||
|
action_id: alloc::string::String,
|
||||||
|
data: StoreAddr,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct TargetedActionRequest {
|
pub struct TargetedActionRequest {
|
||||||
target: TargetId,
|
target: TargetId,
|
||||||
hk_request: ActionRequest,
|
action_request: ActionRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ActionRequestProvider {
|
impl TargetedActionRequest {
|
||||||
fn route_action_request(&self, targeted_request: TargetedActionRequest);
|
pub fn new(target: TargetId, action_request: ActionRequest) -> Self {
|
||||||
|
Self {
|
||||||
|
target,
|
||||||
|
action_request,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use crate::tmtc::TargetId;
|
use crate::{
|
||||||
|
pus::verification::{TcStateAccepted, VerificationToken},
|
||||||
|
TargetId,
|
||||||
|
};
|
||||||
|
|
||||||
pub type CollectionIntervalFactor = u32;
|
pub type CollectionIntervalFactor = u32;
|
||||||
pub type UniqueId = u32;
|
pub type UniqueId = u32;
|
||||||
@ -13,10 +16,25 @@ pub enum HkRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct TargetedHkRequest {
|
pub struct TargetedHkRequest {
|
||||||
target: TargetId,
|
pub target_id: TargetId,
|
||||||
hk_request: HkRequest,
|
pub hk_request: HkRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HkRequestProvider {
|
impl TargetedHkRequest {
|
||||||
fn route_hk_request(&self, targeted_request: 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<TcStateAccepted>,
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ pub mod params;
|
|||||||
pub mod pool;
|
pub mod pool;
|
||||||
pub mod power;
|
pub mod power;
|
||||||
pub mod pus;
|
pub mod pus;
|
||||||
|
pub mod queue;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod res_code;
|
pub mod res_code;
|
||||||
pub mod seq_count;
|
pub mod seq_count;
|
||||||
@ -50,5 +51,8 @@ pub mod tmtc;
|
|||||||
|
|
||||||
pub use spacepackets;
|
pub use spacepackets;
|
||||||
|
|
||||||
// Generic channel ID type.
|
/// Generic channel ID type.
|
||||||
pub type ChannelId = u32;
|
pub type ChannelId = u32;
|
||||||
|
|
||||||
|
/// Generic target ID type.
|
||||||
|
pub type TargetId = u64;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use crate::tmtc::TargetId;
|
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use spacepackets::ByteConversionError;
|
use spacepackets::ByteConversionError;
|
||||||
|
|
||||||
|
use crate::TargetId;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct ModeAndSubmode {
|
pub struct ModeAndSubmode {
|
||||||
|
@ -51,7 +51,6 @@
|
|||||||
//! assert_eq!(example_obj.id, obj_id);
|
//! assert_eq!(example_obj.id, obj_id);
|
||||||
//! assert_eq!(example_obj.dummy, 42);
|
//! assert_eq!(example_obj.dummy, 42);
|
||||||
//! ```
|
//! ```
|
||||||
use crate::tmtc::TargetId;
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
@ -63,6 +62,8 @@ use hashbrown::HashMap;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
use crate::TargetId;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||||
pub struct ObjectId {
|
pub struct ObjectId {
|
||||||
pub id: TargetId,
|
pub id: TargetId,
|
||||||
|
115
satrs/src/pus/action.rs
Normal file
115
satrs/src/pus/action.rs
Normal file
@ -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<TcStateAccepted>,
|
||||||
|
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<TcStateAccepted>,
|
||||||
|
) -> 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<TcInMemConverter>,
|
||||||
|
request_converter: RequestConverter,
|
||||||
|
request_router: RequestRouter,
|
||||||
|
routing_error_handler: RoutingErrorHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
TcInMemConverter: EcssTcInMemConverter,
|
||||||
|
RequestConverter: PusActionToRequestConverter<Error = PusPacketHandlingError>,
|
||||||
|
RequestRouter: PusActionRequestRouter<Error = GenericRoutingError>,
|
||||||
|
RoutingErrorHandler: PusRoutingErrorHandler<Error = GenericRoutingError>,
|
||||||
|
>
|
||||||
|
PusService8ActionHandler<
|
||||||
|
TcInMemConverter,
|
||||||
|
RequestConverter,
|
||||||
|
RequestRouter,
|
||||||
|
RoutingErrorHandler,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
service_helper: PusServiceHelper<TcInMemConverter>,
|
||||||
|
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<PusPacketHandlerResult, PusPacketHandlingError> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,110 @@
|
|||||||
pub use spacepackets::ecss::hk::*;
|
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<TcStateAccepted>,
|
||||||
|
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<TcStateAccepted>,
|
||||||
|
) -> 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<TcInMemConverter>,
|
||||||
|
request_converter: RequestConverter,
|
||||||
|
request_router: RequestRouter,
|
||||||
|
routing_error_handler: RoutingErrorHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
TcInMemConverter: EcssTcInMemConverter,
|
||||||
|
RequestConverter: PusHkToRequestConverter<Error = PusPacketHandlingError>,
|
||||||
|
RequestRouter: PusHkRequestRouter<Error = GenericRoutingError>,
|
||||||
|
RoutingErrorHandler: PusRoutingErrorHandler<Error = GenericRoutingError>,
|
||||||
|
>
|
||||||
|
PusService3HkHandler<TcInMemConverter, RequestConverter, RequestRouter, RoutingErrorHandler>
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
service_helper: PusServiceHelper<TcInMemConverter>,
|
||||||
|
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<PusPacketHandlerResult, PusPacketHandlingError> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
//!
|
//!
|
||||||
//! This module contains structures to make working with the PUS C standard easier.
|
//! This module contains structures to make working with the PUS C standard easier.
|
||||||
//! The satrs-example application contains various usage examples of these components.
|
//! 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};
|
use core::fmt::{Display, Formatter};
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use downcast_rs::{impl_downcast, Downcast};
|
use downcast_rs::{impl_downcast, Downcast};
|
||||||
@ -16,6 +17,7 @@ use spacepackets::ecss::tm::PusTmCreator;
|
|||||||
use spacepackets::ecss::PusError;
|
use spacepackets::ecss::PusError;
|
||||||
use spacepackets::{ByteConversionError, SpHeader};
|
use spacepackets::{ByteConversionError, SpHeader};
|
||||||
|
|
||||||
|
pub mod action;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod event_man;
|
pub mod event_man;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -37,6 +39,8 @@ use crate::pus::verification::{TcStateAccepted, TcStateToken, VerificationToken}
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std_mod::*;
|
pub use std_mod::*;
|
||||||
|
|
||||||
|
use self::verification::VerificationReporterWithSender;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum PusTmWrapper<'tm> {
|
pub enum PusTmWrapper<'tm> {
|
||||||
InStore(StoreAddr),
|
InStore(StoreAddr),
|
||||||
@ -55,52 +59,6 @@ impl<'tm> From<PusTmCreator<'tm>> for PusTmWrapper<'tm> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic error type for sending something via a message queue.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum GenericSendError {
|
|
||||||
RxDisconnected,
|
|
||||||
QueueFull(Option<u32>),
|
|
||||||
}
|
|
||||||
|
|
||||||
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum EcssTmtcError {
|
pub enum EcssTmtcError {
|
||||||
StoreLock,
|
StoreLock,
|
||||||
@ -385,6 +343,19 @@ mod alloc_mod {
|
|||||||
impl<T> EcssTcReceiver for T where T: EcssTcReceiverCore + 'static {}
|
impl<T> EcssTcReceiver for T where T: EcssTcReceiverCore + 'static {}
|
||||||
|
|
||||||
impl_downcast!(EcssTcReceiver);
|
impl_downcast!(EcssTcReceiver);
|
||||||
|
|
||||||
|
pub trait PusRoutingErrorHandler {
|
||||||
|
type Error;
|
||||||
|
fn handle_error(
|
||||||
|
&self,
|
||||||
|
target_id: TargetId,
|
||||||
|
token: VerificationToken<TcStateAccepted>,
|
||||||
|
tc: &PusTcReader,
|
||||||
|
error: GenericRoutingError,
|
||||||
|
time_stamp: &[u8],
|
||||||
|
verif_reporter: &mut VerificationReporterWithSender,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -400,7 +371,7 @@ pub mod std_mod {
|
|||||||
TryRecvTmtcError,
|
TryRecvTmtcError,
|
||||||
};
|
};
|
||||||
use crate::tmtc::tm_helper::SharedTmPool;
|
use crate::tmtc::tm_helper::SharedTmPool;
|
||||||
use crate::ChannelId;
|
use crate::{ChannelId, TargetId};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use crossbeam_channel as cb;
|
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)]
|
#[derive(Debug, Clone, Error)]
|
||||||
pub enum PusPacketHandlingError {
|
pub enum PusPacketHandlingError {
|
||||||
#[error("generic PUS error: {0}")]
|
#[error("generic PUS error: {0}")]
|
||||||
@ -682,6 +667,8 @@ pub mod std_mod {
|
|||||||
EcssTmtc(#[from] EcssTmtcError),
|
EcssTmtc(#[from] EcssTmtcError),
|
||||||
#[error("invalid verification token")]
|
#[error("invalid verification token")]
|
||||||
InvalidVerificationToken,
|
InvalidVerificationToken,
|
||||||
|
#[error("request routing error: {0}")]
|
||||||
|
RequestRoutingError(#[from] GenericRoutingError),
|
||||||
#[error("other error {0}")]
|
#[error("other error {0}")]
|
||||||
Other(String),
|
Other(String),
|
||||||
}
|
}
|
||||||
|
49
satrs/src/queue.rs
Normal file
49
satrs/src/queue.rs
Normal file
@ -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<u32>),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {}
|
@ -1,3 +1,85 @@
|
|||||||
pub trait RequestRouter {
|
use core::fmt;
|
||||||
//fn route_request(&self, request: Request);
|
|
||||||
|
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<Self, TargetIdCreationError> {
|
||||||
|
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<u64> for TargetAndApidId {
|
||||||
|
fn from(raw: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
apid: (raw >> 32) as u16,
|
||||||
|
target: raw as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TargetAndApidId> 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,6 @@ pub use ccsds_distrib::{CcsdsDistributor, CcsdsError, CcsdsPacketHandler};
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub use pus_distrib::{PusDistributor, PusServiceProvider};
|
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
|
/// Generic trait for object which can receive any telecommands in form of a raw bytestream, with
|
||||||
/// no assumptions about the received protocol.
|
/// no assumptions about the received protocol.
|
||||||
///
|
///
|
||||||
|
Loading…
x
Reference in New Issue
Block a user