2024-02-12 12:54:25 +01:00
|
|
|
use super::scheduler::PusSchedulerProvider;
|
2024-02-03 13:41:51 +01:00
|
|
|
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
|
2024-02-10 11:59:26 +01:00
|
|
|
use crate::pool::PoolProvider;
|
2024-02-03 13:41:51 +01:00
|
|
|
use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError};
|
|
|
|
use alloc::string::ToString;
|
2023-07-05 11:25:23 +02:00
|
|
|
use spacepackets::ecss::{scheduling, PusPacket};
|
|
|
|
use spacepackets::time::cds::TimeProvider;
|
2024-01-31 11:40:01 +01:00
|
|
|
|
2023-07-06 01:14:01 +02:00
|
|
|
/// This is a helper class for [std] environments to handle generic PUS 11 (scheduling service)
|
2024-02-03 13:41:51 +01:00
|
|
|
/// packets. This handler is able to handle the most important PUS requests for a scheduling
|
2024-02-12 14:32:32 +01:00
|
|
|
/// service which provides the [PusSchedulerProvider].
|
2023-07-06 01:14:01 +02:00
|
|
|
///
|
|
|
|
/// Please note that this class does not do the regular periodic handling like releasing any
|
|
|
|
/// telecommands inside the scheduler. The user can retrieve the wrapped scheduler via the
|
|
|
|
/// [Self::scheduler] and [Self::scheduler_mut] function and then use the scheduler API to release
|
|
|
|
/// telecommands when applicable.
|
2024-02-03 13:41:51 +01:00
|
|
|
pub struct PusService11SchedHandler<
|
|
|
|
TcInMemConverter: EcssTcInMemConverter,
|
2024-02-12 12:54:25 +01:00
|
|
|
PusScheduler: PusSchedulerProvider,
|
2024-02-03 13:41:51 +01:00
|
|
|
> {
|
|
|
|
pub service_helper: PusServiceHelper<TcInMemConverter>,
|
2024-02-12 12:54:25 +01:00
|
|
|
scheduler: PusScheduler,
|
2023-07-05 11:25:23 +02:00
|
|
|
}
|
|
|
|
|
2024-02-12 12:54:25 +01:00
|
|
|
impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerProvider>
|
2024-02-03 13:41:51 +01:00
|
|
|
PusService11SchedHandler<TcInMemConverter, Scheduler>
|
|
|
|
{
|
|
|
|
pub fn new(service_helper: PusServiceHelper<TcInMemConverter>, scheduler: Scheduler) -> Self {
|
2023-07-05 11:25:23 +02:00
|
|
|
Self {
|
2024-02-03 13:41:51 +01:00
|
|
|
service_helper,
|
2023-07-05 11:25:23 +02:00
|
|
|
scheduler,
|
|
|
|
}
|
|
|
|
}
|
2023-07-05 15:12:03 +02:00
|
|
|
|
2024-02-03 13:41:51 +01:00
|
|
|
pub fn scheduler_mut(&mut self) -> &mut Scheduler {
|
2023-07-05 15:12:03 +02:00
|
|
|
&mut self.scheduler
|
|
|
|
}
|
2023-07-06 01:14:01 +02:00
|
|
|
|
2024-02-03 13:41:51 +01:00
|
|
|
pub fn scheduler(&self) -> &Scheduler {
|
2023-07-06 01:14:01 +02:00
|
|
|
&self.scheduler
|
|
|
|
}
|
2023-07-05 11:25:23 +02:00
|
|
|
|
2024-02-03 13:41:51 +01:00
|
|
|
pub fn handle_one_tc(
|
|
|
|
&mut self,
|
2024-02-10 11:59:26 +01:00
|
|
|
sched_tc_pool: &mut (impl PoolProvider + ?Sized),
|
2024-02-03 13:41:51 +01:00
|
|
|
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
|
|
|
|
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
|
2024-01-30 01:18:48 +01:00
|
|
|
if possible_packet.is_none() {
|
|
|
|
return Ok(PusPacketHandlerResult::Empty);
|
|
|
|
}
|
2024-01-31 01:32:03 +01:00
|
|
|
let ecss_tc_and_token = possible_packet.unwrap();
|
2024-01-31 11:40:01 +01:00
|
|
|
let tc = self
|
2024-02-03 13:41:51 +01:00
|
|
|
.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.tc_in_mem_converter
|
2024-02-03 13:41:51 +01:00
|
|
|
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
|
|
|
|
let subservice = PusPacket::subservice(&tc);
|
|
|
|
let standard_subservice = scheduling::Subservice::try_from(subservice);
|
|
|
|
if standard_subservice.is_err() {
|
2023-07-05 11:58:43 +02:00
|
|
|
return Ok(PusPacketHandlerResult::CustomSubservice(
|
2024-02-03 13:41:51 +01:00
|
|
|
subservice,
|
2024-01-31 01:32:03 +01:00
|
|
|
ecss_tc_and_token.token,
|
2023-07-05 11:58:43 +02:00
|
|
|
));
|
2023-07-05 11:25:23 +02:00
|
|
|
}
|
2023-07-05 21:08:04 +02:00
|
|
|
let mut partial_error = None;
|
2024-01-31 11:40:01 +01:00
|
|
|
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
2024-02-03 13:41:51 +01:00
|
|
|
match standard_subservice.unwrap() {
|
2023-07-05 11:25:23 +02:00
|
|
|
scheduling::Subservice::TcEnableScheduling => {
|
|
|
|
let start_token = self
|
2024-02-03 13:41:51 +01:00
|
|
|
.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.common
|
2023-07-05 11:25:23 +02:00
|
|
|
.verification_handler
|
2023-07-06 00:49:18 +02:00
|
|
|
.get_mut()
|
2024-01-31 01:32:03 +01:00
|
|
|
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("Error sending start success");
|
|
|
|
|
|
|
|
self.scheduler.enable();
|
|
|
|
if self.scheduler.is_enabled() {
|
2024-02-03 13:41:51 +01:00
|
|
|
self.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.common
|
2023-07-05 11:25:23 +02:00
|
|
|
.verification_handler
|
2023-07-06 00:49:18 +02:00
|
|
|
.get_mut()
|
2023-07-05 21:08:04 +02:00
|
|
|
.completion_success(start_token, Some(&time_stamp))
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("Error sending completion success");
|
|
|
|
} else {
|
2024-02-03 13:41:51 +01:00
|
|
|
return Err(PusPacketHandlingError::Other(
|
|
|
|
"failed to enabled scheduler".to_string(),
|
|
|
|
));
|
2023-07-05 11:25:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
scheduling::Subservice::TcDisableScheduling => {
|
|
|
|
let start_token = self
|
2024-02-03 13:41:51 +01:00
|
|
|
.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.common
|
2023-07-05 11:25:23 +02:00
|
|
|
.verification_handler
|
2023-07-06 00:49:18 +02:00
|
|
|
.get_mut()
|
2024-01-31 01:32:03 +01:00
|
|
|
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("Error sending start success");
|
|
|
|
|
|
|
|
self.scheduler.disable();
|
|
|
|
if !self.scheduler.is_enabled() {
|
2024-02-03 13:41:51 +01:00
|
|
|
self.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.common
|
2023-07-05 11:25:23 +02:00
|
|
|
.verification_handler
|
2023-07-06 00:49:18 +02:00
|
|
|
.get_mut()
|
2023-07-05 21:08:04 +02:00
|
|
|
.completion_success(start_token, Some(&time_stamp))
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("Error sending completion success");
|
|
|
|
} else {
|
2024-02-03 13:41:51 +01:00
|
|
|
return Err(PusPacketHandlingError::Other(
|
|
|
|
"failed to disable scheduler".to_string(),
|
|
|
|
));
|
2023-07-05 11:25:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
scheduling::Subservice::TcResetScheduling => {
|
|
|
|
let start_token = self
|
2024-02-03 13:41:51 +01:00
|
|
|
.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.common
|
2023-07-05 11:25:23 +02:00
|
|
|
.verification_handler
|
2023-07-06 00:49:18 +02:00
|
|
|
.get_mut()
|
2024-01-31 01:32:03 +01:00
|
|
|
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("Error sending start success");
|
|
|
|
|
|
|
|
self.scheduler
|
2024-02-03 13:41:51 +01:00
|
|
|
.reset(sched_tc_pool)
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("Error resetting TC Pool");
|
|
|
|
|
2024-02-03 13:41:51 +01:00
|
|
|
self.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.common
|
2023-07-05 11:25:23 +02:00
|
|
|
.verification_handler
|
2023-07-06 00:49:18 +02:00
|
|
|
.get_mut()
|
2023-07-05 21:08:04 +02:00
|
|
|
.completion_success(start_token, Some(&time_stamp))
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("Error sending completion success");
|
|
|
|
}
|
|
|
|
scheduling::Subservice::TcInsertActivity => {
|
|
|
|
let start_token = self
|
2024-02-03 13:41:51 +01:00
|
|
|
.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.common
|
2023-07-05 11:25:23 +02:00
|
|
|
.verification_handler
|
2023-07-06 00:49:18 +02:00
|
|
|
.get_mut()
|
2024-01-31 01:32:03 +01:00
|
|
|
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("error sending start success");
|
|
|
|
|
2024-02-03 13:41:51 +01:00
|
|
|
// let mut pool = self.sched_tc_pool.write().expect("locking pool failed");
|
2023-07-05 11:25:23 +02:00
|
|
|
self.scheduler
|
2024-02-03 13:41:51 +01:00
|
|
|
.insert_wrapped_tc::<TimeProvider>(&tc, sched_tc_pool)
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("insertion of activity into pool failed");
|
|
|
|
|
2024-02-03 13:41:51 +01:00
|
|
|
self.service_helper
|
2024-01-31 11:40:01 +01:00
|
|
|
.common
|
2023-07-05 11:25:23 +02:00
|
|
|
.verification_handler
|
2023-07-06 00:49:18 +02:00
|
|
|
.get_mut()
|
2023-07-05 21:08:04 +02:00
|
|
|
.completion_success(start_token, Some(&time_stamp))
|
2023-07-05 11:25:23 +02:00
|
|
|
.expect("sending completion success failed");
|
|
|
|
}
|
|
|
|
_ => {
|
2024-01-31 01:32:03 +01:00
|
|
|
// Treat unhandled standard subservices as custom subservices for now.
|
2023-07-05 11:58:43 +02:00
|
|
|
return Ok(PusPacketHandlerResult::CustomSubservice(
|
2024-02-03 13:41:51 +01:00
|
|
|
subservice,
|
2024-01-31 11:40:01 +01:00
|
|
|
ecss_tc_and_token.token,
|
2023-07-05 11:58:43 +02:00
|
|
|
));
|
2023-07-05 11:25:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(partial_error) = partial_error {
|
|
|
|
return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
|
|
|
|
partial_error,
|
|
|
|
));
|
|
|
|
}
|
2024-01-31 01:32:03 +01:00
|
|
|
Ok(PusPacketHandlerResult::RequestHandled)
|
2023-07-05 11:25:23 +02:00
|
|
|
}
|
|
|
|
}
|
2024-02-03 13:41:51 +01:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::pool::{StaticMemoryPool, StaticPoolConfig};
|
|
|
|
use crate::pus::tests::TEST_APID;
|
|
|
|
use crate::pus::{
|
2024-02-12 12:54:25 +01:00
|
|
|
scheduler::{self, PusSchedulerProvider, TcInfo},
|
2024-02-03 13:41:51 +01:00
|
|
|
tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness},
|
|
|
|
verification::{RequestId, TcStateAccepted, VerificationToken},
|
|
|
|
EcssTcInSharedStoreConverter,
|
|
|
|
};
|
|
|
|
use alloc::collections::VecDeque;
|
|
|
|
use delegate::delegate;
|
|
|
|
use spacepackets::ecss::scheduling::Subservice;
|
|
|
|
use spacepackets::ecss::tc::PusTcSecondaryHeader;
|
|
|
|
use spacepackets::ecss::WritablePusPacket;
|
|
|
|
use spacepackets::time::TimeWriter;
|
|
|
|
use spacepackets::SpHeader;
|
|
|
|
use spacepackets::{
|
|
|
|
ecss::{tc::PusTcCreator, tm::PusTmReader},
|
|
|
|
time::cds,
|
|
|
|
};
|
|
|
|
|
|
|
|
use super::PusService11SchedHandler;
|
|
|
|
|
|
|
|
struct Pus11HandlerWithStoreTester {
|
|
|
|
common: PusServiceHandlerWithSharedStoreCommon,
|
|
|
|
handler: PusService11SchedHandler<EcssTcInSharedStoreConverter, TestScheduler>,
|
|
|
|
sched_tc_pool: StaticMemoryPool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Pus11HandlerWithStoreTester {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
let test_scheduler = TestScheduler::default();
|
2024-02-12 11:35:10 +01:00
|
|
|
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false);
|
2024-02-03 13:41:51 +01:00
|
|
|
let sched_tc_pool = StaticMemoryPool::new(pool_cfg.clone());
|
|
|
|
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
|
|
|
|
Self {
|
|
|
|
common,
|
|
|
|
handler: PusService11SchedHandler::new(srv_handler, test_scheduler),
|
|
|
|
sched_tc_pool,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PusTestHarness for Pus11HandlerWithStoreTester {
|
|
|
|
delegate! {
|
|
|
|
to self.common {
|
|
|
|
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
|
|
|
|
fn read_next_tm(&mut self) -> PusTmReader<'_>;
|
|
|
|
fn check_no_tm_available(&self) -> bool;
|
|
|
|
fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct TestScheduler {
|
|
|
|
reset_count: u32,
|
|
|
|
enabled: bool,
|
|
|
|
enabled_count: u32,
|
|
|
|
disabled_count: u32,
|
|
|
|
inserted_tcs: VecDeque<TcInfo>,
|
|
|
|
}
|
|
|
|
|
2024-02-12 12:54:25 +01:00
|
|
|
impl PusSchedulerProvider for TestScheduler {
|
2024-02-03 13:41:51 +01:00
|
|
|
type TimeProvider = cds::TimeProvider;
|
|
|
|
|
|
|
|
fn reset(
|
|
|
|
&mut self,
|
2024-02-10 11:59:26 +01:00
|
|
|
_store: &mut (impl crate::pool::PoolProvider + ?Sized),
|
2024-02-03 13:41:51 +01:00
|
|
|
) -> Result<(), crate::pool::StoreError> {
|
|
|
|
self.reset_count += 1;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_enabled(&self) -> bool {
|
|
|
|
self.enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
fn enable(&mut self) {
|
|
|
|
self.enabled_count += 1;
|
|
|
|
self.enabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn disable(&mut self) {
|
|
|
|
self.disabled_count += 1;
|
|
|
|
self.enabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn insert_unwrapped_and_stored_tc(
|
|
|
|
&mut self,
|
|
|
|
_time_stamp: spacepackets::time::UnixTimestamp,
|
|
|
|
info: crate::pus::scheduler::TcInfo,
|
|
|
|
) -> Result<(), crate::pus::scheduler::ScheduleError> {
|
|
|
|
self.inserted_tcs.push_back(info);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generic_subservice_send(
|
|
|
|
test_harness: &mut Pus11HandlerWithStoreTester,
|
|
|
|
subservice: Subservice,
|
|
|
|
) {
|
|
|
|
let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap();
|
|
|
|
let tc_header = PusTcSecondaryHeader::new_simple(11, subservice as u8);
|
|
|
|
let enable_scheduling = PusTcCreator::new(&mut reply_header, tc_header, &[0; 7], true);
|
|
|
|
let token = test_harness.send_tc(&enable_scheduling);
|
|
|
|
|
|
|
|
let request_id = token.req_id();
|
|
|
|
test_harness
|
|
|
|
.handler
|
|
|
|
.handle_one_tc(&mut test_harness.sched_tc_pool)
|
|
|
|
.unwrap();
|
|
|
|
test_harness.check_next_verification_tm(1, request_id);
|
|
|
|
test_harness.check_next_verification_tm(3, request_id);
|
|
|
|
test_harness.check_next_verification_tm(7, request_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_scheduling_enabling_tc() {
|
|
|
|
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
|
|
|
test_harness.handler.scheduler_mut().disable();
|
|
|
|
assert!(!test_harness.handler.scheduler().is_enabled());
|
|
|
|
generic_subservice_send(&mut test_harness, Subservice::TcEnableScheduling);
|
|
|
|
assert!(test_harness.handler.scheduler().is_enabled());
|
|
|
|
assert_eq!(test_harness.handler.scheduler().enabled_count, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_scheduling_disabling_tc() {
|
|
|
|
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
|
|
|
test_harness.handler.scheduler_mut().enable();
|
|
|
|
assert!(test_harness.handler.scheduler().is_enabled());
|
|
|
|
generic_subservice_send(&mut test_harness, Subservice::TcDisableScheduling);
|
|
|
|
assert!(!test_harness.handler.scheduler().is_enabled());
|
|
|
|
assert_eq!(test_harness.handler.scheduler().disabled_count, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_reset_scheduler_tc() {
|
|
|
|
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
|
|
|
generic_subservice_send(&mut test_harness, Subservice::TcResetScheduling);
|
|
|
|
assert_eq!(test_harness.handler.scheduler().reset_count, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_insert_activity_tc() {
|
|
|
|
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
|
|
|
let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap();
|
|
|
|
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
|
|
|
let ping_tc = PusTcCreator::new(&mut reply_header, sec_header, &[], true);
|
|
|
|
let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc);
|
|
|
|
let stamper = cds::TimeProvider::from_now_with_u16_days().expect("time provider failed");
|
|
|
|
let mut sched_app_data: [u8; 64] = [0; 64];
|
|
|
|
let mut written_len = stamper.write_to_bytes(&mut sched_app_data).unwrap();
|
|
|
|
let ping_raw = ping_tc.to_vec().expect("generating raw tc failed");
|
|
|
|
sched_app_data[written_len..written_len + ping_raw.len()].copy_from_slice(&ping_raw);
|
|
|
|
written_len += ping_raw.len();
|
|
|
|
reply_header = SpHeader::tm_unseg(TEST_APID, 1, 0).unwrap();
|
|
|
|
sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8);
|
|
|
|
let enable_scheduling = PusTcCreator::new(
|
|
|
|
&mut reply_header,
|
|
|
|
sec_header,
|
|
|
|
&sched_app_data[..written_len],
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
let token = test_harness.send_tc(&enable_scheduling);
|
|
|
|
|
|
|
|
let request_id = token.req_id();
|
|
|
|
test_harness
|
|
|
|
.handler
|
|
|
|
.handle_one_tc(&mut test_harness.sched_tc_pool)
|
|
|
|
.unwrap();
|
|
|
|
test_harness.check_next_verification_tm(1, request_id);
|
|
|
|
test_harness.check_next_verification_tm(3, request_id);
|
|
|
|
test_harness.check_next_verification_tm(7, request_id);
|
|
|
|
let tc_info = test_harness
|
|
|
|
.handler
|
|
|
|
.scheduler_mut()
|
|
|
|
.inserted_tcs
|
|
|
|
.pop_front()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(tc_info.request_id(), req_id_ping_tc);
|
|
|
|
}
|
|
|
|
}
|