Compare commits
	
		
			2 Commits
		
	
	
		
			ccsds-sche
			...
			bump-space
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					
						
						
							
						
						45a99cabce
	
				 | 
					
					
						||
| 
						 | 
					
						
						
							
						
						ab44e3312c
	
				 | 
					
					
						
							
								
								
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -47,8 +47,6 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - uses: dtolnay/rust-toolchain@stable
 | 
			
		||||
        with:
 | 
			
		||||
          components: rustfmt
 | 
			
		||||
      - run: cargo fmt --all -- --check
 | 
			
		||||
 | 
			
		||||
  docs:
 | 
			
		||||
@@ -57,7 +55,7 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - uses: dtolnay/rust-toolchain@nightly
 | 
			
		||||
      - run: RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc -p satrs --all-features
 | 
			
		||||
      - run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
 | 
			
		||||
 | 
			
		||||
  clippy:
 | 
			
		||||
    name: Clippy
 | 
			
		||||
@@ -65,6 +63,4 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - uses: dtolnay/rust-toolchain@stable
 | 
			
		||||
        with:
 | 
			
		||||
          components: clippy
 | 
			
		||||
      - run: cargo clippy -- -D warnings
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								justfile
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								justfile
									
									
									
									
									
								
							@@ -1,23 +0,0 @@
 | 
			
		||||
all: check embedded test fmt clippy docs
 | 
			
		||||
 | 
			
		||||
check:
 | 
			
		||||
  cargo check
 | 
			
		||||
  cargo check -p satrs-example --no-default-features
 | 
			
		||||
 | 
			
		||||
test:
 | 
			
		||||
  cargo nextest run --all-features
 | 
			
		||||
  cargo test --doc --all-features
 | 
			
		||||
 | 
			
		||||
embedded:
 | 
			
		||||
  cargo check -p satrs --target=thumbv7em-none-eabihf --no-default-features
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
  cargo fmt --all -- --check
 | 
			
		||||
 | 
			
		||||
clippy:
 | 
			
		||||
  cargo clippy -- -D warnings
 | 
			
		||||
 | 
			
		||||
docs-satrs:
 | 
			
		||||
  RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p satrs --all-features
 | 
			
		||||
 | 
			
		||||
docs: docs-satrs
 | 
			
		||||
@@ -11,6 +11,8 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
 | 
			
		||||
fern = "0.7"
 | 
			
		||||
chrono = "0.4"
 | 
			
		||||
log = "0.4"
 | 
			
		||||
arbitrary-int = "2"
 | 
			
		||||
bitbybit = "1.4"
 | 
			
		||||
crossbeam-channel = "0.5"
 | 
			
		||||
delegate = "0.13"
 | 
			
		||||
zerocopy = "0.8"
 | 
			
		||||
@@ -21,8 +23,6 @@ lazy_static = "1"
 | 
			
		||||
strum = { version = "0.27", features = ["derive"] }
 | 
			
		||||
derive-new = "0.7"
 | 
			
		||||
cfg-if = "1"
 | 
			
		||||
arbitrary-int = "2"
 | 
			
		||||
bitbybit = "1.4"
 | 
			
		||||
serde = { version = "1", features = ["derive"] }
 | 
			
		||||
serde_json = "1"
 | 
			
		||||
 | 
			
		||||
@@ -38,8 +38,8 @@ version = "0.1.1"
 | 
			
		||||
path = "../satrs-mib"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
default = ["heap_tmtc"]
 | 
			
		||||
heap_tmtc = []
 | 
			
		||||
default = ["heap_tmtc"]
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
env_logger = "0.11"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
use arbitrary_int::u11;
 | 
			
		||||
use lazy_static::lazy_static;
 | 
			
		||||
use satrs::{
 | 
			
		||||
    res_code::ResultU16,
 | 
			
		||||
@@ -11,7 +10,7 @@ use strum::IntoEnumIterator;
 | 
			
		||||
 | 
			
		||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
 | 
			
		||||
use satrs::{
 | 
			
		||||
    events_legacy::{EventU32TypedSev, SeverityInfo},
 | 
			
		||||
    events::{EventU32TypedSev, SeverityInfo},
 | 
			
		||||
    pool::{StaticMemoryPool, StaticPoolConfig},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -45,7 +44,7 @@ lazy_static! {
 | 
			
		||||
    pub static ref PACKET_ID_VALIDATOR: HashSet<PacketId> = {
 | 
			
		||||
        let mut set = HashSet::new();
 | 
			
		||||
        for id in crate::ids::Apid::iter() {
 | 
			
		||||
            set.insert(PacketId::new(PacketType::Tc, true, u11::new(id as u16)));
 | 
			
		||||
            set.insert(PacketId::new(PacketType::Tc, true, id.raw_value()));
 | 
			
		||||
        }
 | 
			
		||||
        set
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,14 @@
 | 
			
		||||
use std::sync::mpsc::{self};
 | 
			
		||||
 | 
			
		||||
use crate::pus::create_verification_reporter;
 | 
			
		||||
use arbitrary_int::traits::Integer as _;
 | 
			
		||||
use arbitrary_int::u11;
 | 
			
		||||
use satrs::event_man_legacy::{EventMessageU32, EventRoutingError};
 | 
			
		||||
use satrs::event_man::{EventMessageU32, EventRoutingError};
 | 
			
		||||
use satrs::pus::event::EventTmHook;
 | 
			
		||||
use satrs::pus::verification::VerificationReporter;
 | 
			
		||||
use satrs::pus::EcssTmSender;
 | 
			
		||||
use satrs::request::UniqueApidTargetId;
 | 
			
		||||
use satrs::{
 | 
			
		||||
    event_man_legacy::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
 | 
			
		||||
    event_man::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
 | 
			
		||||
    pus::{
 | 
			
		||||
        event_man::{
 | 
			
		||||
            DefaultPusEventU32TmCreator, EventReporter, EventRequest, EventRequestWithToken,
 | 
			
		||||
@@ -61,11 +60,12 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
 | 
			
		||||
        // telemetry for each event.
 | 
			
		||||
        let event_reporter = EventReporter::new_with_hook(
 | 
			
		||||
            PUS_EVENT_MANAGEMENT.raw(),
 | 
			
		||||
            u11::ZERO,
 | 
			
		||||
            u11::new(0),
 | 
			
		||||
            0,
 | 
			
		||||
            128,
 | 
			
		||||
            EventApidSetter::default(),
 | 
			
		||||
        );
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let pus_event_dispatcher =
 | 
			
		||||
            DefaultPusEventU32TmCreator::new_with_default_backend(event_reporter);
 | 
			
		||||
        let pus_event_man_send_provider = EventU32SenderMpscBounded::new(
 | 
			
		||||
@@ -219,7 +219,7 @@ impl<TmSender: EcssTmSender> EventHandler<TmSender> {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use satrs::{
 | 
			
		||||
        events_legacy::EventU32,
 | 
			
		||||
        events::EventU32,
 | 
			
		||||
        pus::verification::VerificationReporterConfig,
 | 
			
		||||
        spacepackets::ecss::{tm::PusTmReader, PusPacket},
 | 
			
		||||
        tmtc::PacketAsVec,
 | 
			
		||||
@@ -228,7 +228,7 @@ mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(u11::new(1), 2);
 | 
			
		||||
    const TEST_EVENT: EventU32 = EventU32::new(satrs::events_legacy::Severity::Info, 1, 1);
 | 
			
		||||
    const TEST_EVENT: EventU32 = EventU32::new(satrs::events::Severity::Info, 1, 1);
 | 
			
		||||
 | 
			
		||||
    pub struct EventManagementTestbench {
 | 
			
		||||
        pub event_tx: mpsc::SyncSender<EventMessageU32>,
 | 
			
		||||
@@ -242,7 +242,8 @@ mod tests {
 | 
			
		||||
            let (event_tx, event_rx) = mpsc::sync_channel(10);
 | 
			
		||||
            let (_event_req_tx, event_req_rx) = mpsc::sync_channel(10);
 | 
			
		||||
            let (tm_sender, tm_receiver) = mpsc::channel();
 | 
			
		||||
            let verif_reporter_cfg = VerificationReporterConfig::new(u11::new(0x05), 2, 2, 128);
 | 
			
		||||
            let verif_reporter_cfg =
 | 
			
		||||
                VerificationReporterConfig::new(u11::new(0x05), 2, 2, 128).unwrap();
 | 
			
		||||
            let verif_reporter =
 | 
			
		||||
                VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg);
 | 
			
		||||
            let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
 | 
			
		||||
@@ -268,7 +269,7 @@ mod tests {
 | 
			
		||||
            .event_tx
 | 
			
		||||
            .send(EventMessageU32::new(
 | 
			
		||||
                TEST_CREATOR_ID.id(),
 | 
			
		||||
                EventU32::new(satrs::events_legacy::Severity::Info, 1, 1),
 | 
			
		||||
                EventU32::new(satrs::events::Severity::Info, 1, 1),
 | 
			
		||||
            ))
 | 
			
		||||
            .expect("failed to send event");
 | 
			
		||||
        testbench.pus_event_handler.handle_event_requests();
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
use satrs::request::UniqueApidTargetId;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, strum::EnumIter)]
 | 
			
		||||
#[bitbybit::bitenum(u11)]
 | 
			
		||||
#[bitbybit::bitenum(u11, exhaustive = false)]
 | 
			
		||||
pub enum Apid {
 | 
			
		||||
    Sched = 1,
 | 
			
		||||
    GenericPus = 2,
 | 
			
		||||
@@ -13,7 +13,6 @@ pub enum Apid {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub mod acs {
 | 
			
		||||
 | 
			
		||||
    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 | 
			
		||||
    pub enum Id {
 | 
			
		||||
        Subsystem = 1,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,13 @@
 | 
			
		||||
#![allow(dead_code)]
 | 
			
		||||
use std::net::{SocketAddr, UdpSocket};
 | 
			
		||||
use std::sync::mpsc;
 | 
			
		||||
 | 
			
		||||
use log::{info, warn};
 | 
			
		||||
use satrs::hal::std::udp_server::{ReceiveResult, UdpTcServer};
 | 
			
		||||
use satrs::pus::HandlingStatus;
 | 
			
		||||
use satrs::tmtc::{PacketAsVec, StoreAndSendError};
 | 
			
		||||
 | 
			
		||||
use satrs::pool::{PoolProviderWithGuards, SharedStaticMemoryPool};
 | 
			
		||||
use satrs::tmtc::PacketInPool;
 | 
			
		||||
use satrs::tmtc::{PacketAsVec, PacketInPool, StoreAndSendError};
 | 
			
		||||
use satrs::{
 | 
			
		||||
    hal::std::udp_server::{ReceiveResult, UdpTcServer},
 | 
			
		||||
    pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::tmtc::sender::TmTcSender;
 | 
			
		||||
 | 
			
		||||
@@ -16,6 +15,7 @@ pub trait UdpTmHandler {
 | 
			
		||||
    fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
pub struct StaticUdpTmHandler {
 | 
			
		||||
    pub tm_rx: mpsc::Receiver<PacketInPool>,
 | 
			
		||||
    pub tm_store: SharedStaticMemoryPool,
 | 
			
		||||
 
 | 
			
		||||
@@ -456,6 +456,7 @@ mod tests {
 | 
			
		||||
        app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
 | 
			
		||||
        let pus8_packet =
 | 
			
		||||
            PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
 | 
			
		||||
 | 
			
		||||
        testbench.add_tc(&pus8_packet);
 | 
			
		||||
        let time_stamp: [u8; 7] = [0; 7];
 | 
			
		||||
        testbench.verify_next_tc_is_handled_properly(&time_stamp);
 | 
			
		||||
 
 | 
			
		||||
@@ -302,7 +302,7 @@ impl TargetedPusService for HkServiceWrapper {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use arbitrary_int::traits::Integer as _;
 | 
			
		||||
    use arbitrary_int::traits::Integer;
 | 
			
		||||
    use arbitrary_int::u14;
 | 
			
		||||
    use satrs::pus::test_util::{
 | 
			
		||||
        TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1, TEST_UNIQUE_ID_0, TEST_UNIQUE_ID_1,
 | 
			
		||||
@@ -327,11 +327,12 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    use super::{HkReply, HkReplyHandler, HkRequestConverter};
 | 
			
		||||
 | 
			
		||||
    pub const ZERO_SEQ: u14 = u14::ZERO;
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn hk_converter_one_shot_req() {
 | 
			
		||||
        let mut hk_bench =
 | 
			
		||||
            PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, ZERO_SEQ, 0);
 | 
			
		||||
        let target_id = TEST_UNIQUE_ID_0;
 | 
			
		||||
        let unique_id = 5_u32;
 | 
			
		||||
        let mut app_data: [u8; 8] = [0; 8];
 | 
			
		||||
@@ -361,7 +362,7 @@ mod tests {
 | 
			
		||||
    fn hk_converter_enable_periodic_generation() {
 | 
			
		||||
        let mut hk_bench =
 | 
			
		||||
            PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, ZERO_SEQ, 0);
 | 
			
		||||
        let target_id = TEST_UNIQUE_ID_0;
 | 
			
		||||
        let unique_id = 5_u32;
 | 
			
		||||
        let mut app_data: [u8; 8] = [0; 8];
 | 
			
		||||
@@ -400,7 +401,7 @@ mod tests {
 | 
			
		||||
    fn hk_conversion_disable_periodic_generation() {
 | 
			
		||||
        let mut hk_bench =
 | 
			
		||||
            PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, ZERO_SEQ, 0);
 | 
			
		||||
        let target_id = TEST_UNIQUE_ID_0;
 | 
			
		||||
        let unique_id = 5_u32;
 | 
			
		||||
        let mut app_data: [u8; 8] = [0; 8];
 | 
			
		||||
@@ -439,7 +440,7 @@ mod tests {
 | 
			
		||||
    fn hk_conversion_modify_interval() {
 | 
			
		||||
        let mut hk_bench =
 | 
			
		||||
            PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), HkRequestConverter::default());
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, ZERO_SEQ, 0);
 | 
			
		||||
        let target_id = TEST_UNIQUE_ID_0;
 | 
			
		||||
        let unique_id = 5_u32;
 | 
			
		||||
        let mut app_data: [u8; 12] = [0; 12];
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ pub mod stack;
 | 
			
		||||
pub mod test;
 | 
			
		||||
 | 
			
		||||
pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter {
 | 
			
		||||
    let verif_cfg = VerificationReporterConfig::new(apid, 1, 2, 8);
 | 
			
		||||
    let verif_cfg = VerificationReporterConfig::new(apid, 1, 2, 8).unwrap();
 | 
			
		||||
    // Every software component which needs to generate verification telemetry, gets a cloned
 | 
			
		||||
    // verification reporter.
 | 
			
		||||
    VerificationReporter::new(owner_id, &verif_cfg)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
use arbitrary_int::traits::Integer as _;
 | 
			
		||||
use arbitrary_int::u14;
 | 
			
		||||
use derive_new::new;
 | 
			
		||||
use satrs::mode_tree::{ModeNode, ModeParent};
 | 
			
		||||
@@ -80,7 +79,8 @@ impl PusReplyHandler<ActivePusRequestStd, ModeReply> for ModeReplyHandler {
 | 
			
		||||
                    .write_to_be_bytes(&mut source_data)
 | 
			
		||||
                    .expect("writing mode reply failed");
 | 
			
		||||
                let req_id = verification::RequestId::from(reply.request_id());
 | 
			
		||||
                let sp_header = SpHeader::new_for_unseg_tm(req_id.packet_id().apid(), u14::ZERO, 0);
 | 
			
		||||
                let sp_header =
 | 
			
		||||
                    SpHeader::new_for_unseg_tm(req_id.packet_id().apid(), u14::new(0), 0);
 | 
			
		||||
                let sec_header =
 | 
			
		||||
                    PusTmSecondaryHeader::new(200, Subservice::TmModeReply as u8, 0, 0, time_stamp);
 | 
			
		||||
                let pus_tm = PusTmCreator::new(
 | 
			
		||||
@@ -298,7 +298,6 @@ impl TargetedPusService for ModeServiceWrapper {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use arbitrary_int::traits::Integer;
 | 
			
		||||
    use arbitrary_int::u14;
 | 
			
		||||
    use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0, TEST_UNIQUE_ID_0};
 | 
			
		||||
    use satrs::request::MessageMetadata;
 | 
			
		||||
@@ -325,7 +324,7 @@ mod tests {
 | 
			
		||||
    fn mode_converter_read_mode_request() {
 | 
			
		||||
        let mut testbench =
 | 
			
		||||
            PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcReadMode as u8);
 | 
			
		||||
        let mut app_data: [u8; 4] = [0; 4];
 | 
			
		||||
        app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
 | 
			
		||||
@@ -341,7 +340,7 @@ mod tests {
 | 
			
		||||
    fn mode_converter_set_mode_request() {
 | 
			
		||||
        let mut testbench =
 | 
			
		||||
            PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcSetMode as u8);
 | 
			
		||||
        let mut app_data: [u8; 4 + ModeAndSubmode::RAW_LEN] = [0; 4 + ModeAndSubmode::RAW_LEN];
 | 
			
		||||
        let mode_and_submode = ModeAndSubmode::new(2, 1);
 | 
			
		||||
@@ -367,7 +366,7 @@ mod tests {
 | 
			
		||||
    fn mode_converter_announce_mode() {
 | 
			
		||||
        let mut testbench =
 | 
			
		||||
            PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceMode as u8);
 | 
			
		||||
        let mut app_data: [u8; 4] = [0; 4];
 | 
			
		||||
        app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
 | 
			
		||||
@@ -383,7 +382,7 @@ mod tests {
 | 
			
		||||
    fn mode_converter_announce_mode_recursively() {
 | 
			
		||||
        let mut testbench =
 | 
			
		||||
            PusConverterTestbench::new(TEST_COMPONENT_ID_0.id(), ModeRequestConverter::default());
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
 | 
			
		||||
        let sec_header =
 | 
			
		||||
            PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceModeRecursive as u8);
 | 
			
		||||
        let mut app_data: [u8; 4] = [0; 4];
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ use crate::pus::create_verification_reporter;
 | 
			
		||||
use crate::tmtc::sender::TmTcSender;
 | 
			
		||||
use log::info;
 | 
			
		||||
use satrs::pool::{PoolProvider, StaticMemoryPool};
 | 
			
		||||
use satrs::pus::scheduler::{PusSchedulerAlloc, TcInfo};
 | 
			
		||||
use satrs::pus::scheduler::{PusScheduler, TcInfo};
 | 
			
		||||
use satrs::pus::scheduler_srv::PusSchedServiceHandler;
 | 
			
		||||
use satrs::pus::verification::VerificationReporter;
 | 
			
		||||
use satrs::pus::{
 | 
			
		||||
@@ -86,7 +86,7 @@ pub struct SchedulingServiceWrapper {
 | 
			
		||||
        TmTcSender,
 | 
			
		||||
        EcssTcCacher,
 | 
			
		||||
        VerificationReporter,
 | 
			
		||||
        PusSchedulerAlloc,
 | 
			
		||||
        PusScheduler,
 | 
			
		||||
    >,
 | 
			
		||||
    pub sched_tc_pool: StaticMemoryPool,
 | 
			
		||||
    pub releaser_buf: [u8; 4096],
 | 
			
		||||
@@ -179,7 +179,7 @@ pub fn create_scheduler_service(
 | 
			
		||||
    pus_sched_rx: mpsc::Receiver<EcssTcAndToken>,
 | 
			
		||||
    sched_tc_pool: StaticMemoryPool,
 | 
			
		||||
) -> SchedulingServiceWrapper {
 | 
			
		||||
    let scheduler = PusSchedulerAlloc::new_with_current_init_time(Duration::from_secs(5))
 | 
			
		||||
    let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
 | 
			
		||||
        .expect("Creating PUS Scheduler failed");
 | 
			
		||||
    let pus_11_handler = PusSchedServiceHandler::new(
 | 
			
		||||
        PusServiceHelper::new(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
use crate::pus::create_verification_reporter;
 | 
			
		||||
use crate::tmtc::sender::TmTcSender;
 | 
			
		||||
use log::info;
 | 
			
		||||
use satrs::event_man_legacy::{EventMessage, EventMessageU32};
 | 
			
		||||
use satrs::event_man::{EventMessage, EventMessageU32};
 | 
			
		||||
use satrs::pus::test::PusService17TestHandler;
 | 
			
		||||
use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider};
 | 
			
		||||
use satrs::pus::PartialPusHandlingError;
 | 
			
		||||
 
 | 
			
		||||
@@ -9,11 +9,13 @@ use satrs::{
 | 
			
		||||
    pool::PoolProvider,
 | 
			
		||||
    spacepackets::{
 | 
			
		||||
        ecss::{tm::PusTmZeroCopyWriter, PusPacket},
 | 
			
		||||
        seq_count::SequenceCounter,
 | 
			
		||||
        seq_count::SequenceCounterCcsdsSimple,
 | 
			
		||||
        time::cds::MIN_CDS_FIELD_LEN,
 | 
			
		||||
        CcsdsPacket,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
use satrs::{
 | 
			
		||||
    spacepackets::seq_count::SequenceCounter,
 | 
			
		||||
    tmtc::{PacketAsVec, PacketInPool, SharedPacketPool},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ license = "Apache-2.0"
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
spacepackets = { version = ">=0.14, <=0.16", default-features = false }
 | 
			
		||||
spacepackets = { git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", version = ">=0.14, <=0.16", default-features = false }
 | 
			
		||||
 | 
			
		||||
[dependencies.serde]
 | 
			
		||||
version = "1"
 | 
			
		||||
 
 | 
			
		||||
@@ -8,10 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 | 
			
		||||
 | 
			
		||||
# [unreleased]
 | 
			
		||||
 | 
			
		||||
# [v0.3.0-alpha.3] 2025-09-??
 | 
			
		||||
 | 
			
		||||
- Bump `sat-rs` edition to 2024.
 | 
			
		||||
- Bumped `spacepackets` to v0.16
 | 
			
		||||
 | 
			
		||||
## Changed
 | 
			
		||||
 | 
			
		||||
@@ -222,8 +219,7 @@ docs-rs hotfix
 | 
			
		||||
 | 
			
		||||
Initial release.
 | 
			
		||||
 | 
			
		||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.3...HEAD
 | 
			
		||||
[v0.3.0-alpha.3]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.2...satrs-v0.3.0-alpha.3
 | 
			
		||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.2...HEAD
 | 
			
		||||
[v0.3.0-alpha.2]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.1...satrs-v0.3.0-alpha.2
 | 
			
		||||
[v0.3.0-alpha.1]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.0...satrs-v0.3.0-alpha.1
 | 
			
		||||
[v0.3.0-alpha.0]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.2.1...satrs-v0.3.0-alpha.0
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,9 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
satrs-shared = { version = "0.2", path = "../satrs-shared" }
 | 
			
		||||
spacepackets = { version = "0.16", default-features = false }
 | 
			
		||||
spacepackets = { git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", version = "0.16", default-features = false }
 | 
			
		||||
 | 
			
		||||
arbitrary-int = "2"
 | 
			
		||||
delegate = ">0.7, <=0.13"
 | 
			
		||||
paste = "1"
 | 
			
		||||
derive-new = ">=0.6, <=0.7"
 | 
			
		||||
@@ -24,16 +25,14 @@ cobs = { version = "0.4", default-features = false }
 | 
			
		||||
thiserror = { version = "2", default-features = false }
 | 
			
		||||
 | 
			
		||||
hashbrown = { version = ">=0.14, <=0.15", optional = true }
 | 
			
		||||
static_cell = { version = "2" }
 | 
			
		||||
heapless = { version = "0.9", optional = true }
 | 
			
		||||
static_cell = { version = "2", optional = true }
 | 
			
		||||
dyn-clone = { version = "1", optional = true }
 | 
			
		||||
heapless = { version = "0.9", optional = true }
 | 
			
		||||
downcast-rs = { version = "2", default-features = false, optional = true }
 | 
			
		||||
bus = { version = "2.2", optional = true }
 | 
			
		||||
crossbeam-channel = { version = "0.5", default-features = false, optional = true }
 | 
			
		||||
postcard = { version = "1", features = ["alloc"] }
 | 
			
		||||
serde = { version = "1", default-features = false, optional = true }
 | 
			
		||||
socket2 = { version = "0.6", features = ["all"], optional = true }
 | 
			
		||||
arbitrary-int = "2"
 | 
			
		||||
mio = { version = "1", features = ["os-poll", "net"], optional = true }
 | 
			
		||||
defmt = { version = "1", optional = true }
 | 
			
		||||
 | 
			
		||||
@@ -49,7 +48,7 @@ tempfile = "3"
 | 
			
		||||
version = "1"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
default = ["std", "heapless"]
 | 
			
		||||
default = ["std"]
 | 
			
		||||
std = [
 | 
			
		||||
    "downcast-rs/std",
 | 
			
		||||
    "alloc",
 | 
			
		||||
@@ -71,7 +70,7 @@ alloc = [
 | 
			
		||||
]
 | 
			
		||||
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
 | 
			
		||||
crossbeam = ["crossbeam-channel"]
 | 
			
		||||
# heapless = ["dep:heapless", "static_cell"]
 | 
			
		||||
heapless = ["dep:heapless", "static_cell"]
 | 
			
		||||
defmt = ["dep:defmt", "spacepackets/defmt"]
 | 
			
		||||
test_util = []
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
pub mod scheduler;
 | 
			
		||||
@@ -1,129 +0,0 @@
 | 
			
		||||
use core::{hash::Hash, time::Duration};
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
pub use alloc_mod::*;
 | 
			
		||||
use spacepackets::{
 | 
			
		||||
    ByteConversionError, PacketId, PacketSequenceControl,
 | 
			
		||||
    time::{TimestampError, UnixTime},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
pub enum ScheduleError {
 | 
			
		||||
    /// The release time is within the time-margin added on top of the current time.
 | 
			
		||||
    /// The first parameter is the current time, the second one the time margin, and the third one
 | 
			
		||||
    /// the release time.
 | 
			
		||||
    #[error("release time in margin")]
 | 
			
		||||
    ReleaseTimeInTimeMargin {
 | 
			
		||||
        current_time: UnixTime,
 | 
			
		||||
        time_margin: Duration,
 | 
			
		||||
        release_time: UnixTime,
 | 
			
		||||
    },
 | 
			
		||||
    /// Nested time-tagged commands are not allowed.
 | 
			
		||||
    #[error("nested scheduled tc")]
 | 
			
		||||
    NestedScheduledTc,
 | 
			
		||||
    #[error("tc data empty")]
 | 
			
		||||
    TcDataEmpty,
 | 
			
		||||
    #[error("timestamp error: {0}")]
 | 
			
		||||
    TimestampError(#[from] TimestampError),
 | 
			
		||||
    #[error("wrong subservice number {0}")]
 | 
			
		||||
    WrongSubservice(u8),
 | 
			
		||||
    #[error("wrong service number {0}")]
 | 
			
		||||
    WrongService(u8),
 | 
			
		||||
    #[error("byte conversion error: {0}")]
 | 
			
		||||
    ByteConversionError(#[from] ByteConversionError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Clone)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
pub struct CcsdsPacketId {
 | 
			
		||||
    pub packet_id: PacketId,
 | 
			
		||||
    pub psc: PacketSequenceControl,
 | 
			
		||||
    pub crc16: u16,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Hash for CcsdsPacketId {
 | 
			
		||||
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.packet_id.hash(state);
 | 
			
		||||
        self.psc.raw().hash(state);
 | 
			
		||||
        self.crc16.hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub mod alloc_mod {
 | 
			
		||||
    use core::time::Duration;
 | 
			
		||||
    #[cfg(feature = "std")]
 | 
			
		||||
    use std::time::SystemTimeError;
 | 
			
		||||
 | 
			
		||||
    use spacepackets::time::UnixTime;
 | 
			
		||||
 | 
			
		||||
    use crate::ccsds::scheduler::CcsdsPacketId;
 | 
			
		||||
 | 
			
		||||
    pub struct CcsdsScheduler {
 | 
			
		||||
        tc_map: alloc::collections::BTreeMap<
 | 
			
		||||
            UnixTime,
 | 
			
		||||
            alloc::vec::Vec<(CcsdsPacketId, alloc::vec::Vec<u8>)>,
 | 
			
		||||
        >,
 | 
			
		||||
        packet_limit: usize,
 | 
			
		||||
        pub(crate) current_time: UnixTime,
 | 
			
		||||
        time_margin: Duration,
 | 
			
		||||
        enabled: bool,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl CcsdsScheduler {
 | 
			
		||||
        pub fn new(current_time: UnixTime, packet_limit: usize, time_margin: Duration) -> Self {
 | 
			
		||||
            Self {
 | 
			
		||||
                tc_map: alloc::collections::BTreeMap::new(),
 | 
			
		||||
                packet_limit,
 | 
			
		||||
                current_time,
 | 
			
		||||
                time_margin,
 | 
			
		||||
                enabled: true,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// Like [Self::new], but sets the `init_current_time` parameter to the current system time.
 | 
			
		||||
        #[cfg(feature = "std")]
 | 
			
		||||
        pub fn new_with_current_init_time(
 | 
			
		||||
            packet_limit: usize,
 | 
			
		||||
            time_margin: Duration,
 | 
			
		||||
        ) -> Result<Self, SystemTimeError> {
 | 
			
		||||
            Ok(Self::new(UnixTime::now()?, packet_limit, time_margin))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn num_of_entries(&self) -> usize {
 | 
			
		||||
            self.tc_map
 | 
			
		||||
                .values()
 | 
			
		||||
                .map(|v| v.iter().map(|(_, v)| v.len()).sum::<usize>())
 | 
			
		||||
                .sum()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[inline]
 | 
			
		||||
        pub fn enable(&mut self) {
 | 
			
		||||
            self.enabled = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[inline]
 | 
			
		||||
        pub fn disable(&mut self) {
 | 
			
		||||
            self.enabled = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[inline]
 | 
			
		||||
        pub fn update_time(&mut self, current_time: UnixTime) {
 | 
			
		||||
            self.current_time = current_time;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[inline]
 | 
			
		||||
        pub fn current_time(&self) -> &UnixTime {
 | 
			
		||||
            &self.current_time
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO: Implementation
 | 
			
		||||
        pub fn insert_telecommand(
 | 
			
		||||
            &mut self,
 | 
			
		||||
            packet_id: CcsdsPacketId,
 | 
			
		||||
            packet: alloc::vec::Vec<u8>,
 | 
			
		||||
            release_time: UnixTime,
 | 
			
		||||
        ) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use spacepackets::SpHeader;
 | 
			
		||||
use spacepackets::{CcsdsPacket, SpHeader};
 | 
			
		||||
 | 
			
		||||
use crate::{ComponentId, tmtc::PacketSenderRaw};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,850 +0,0 @@
 | 
			
		||||
//! # Event management and forwarding
 | 
			
		||||
//!
 | 
			
		||||
//! This is a legacy module. It is recommended to use [super::event_man] instead.
 | 
			
		||||
//!
 | 
			
		||||
//! It is recommended to read the
 | 
			
		||||
//! [sat-rs book chapter](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/events.html)
 | 
			
		||||
//! about events first.
 | 
			
		||||
//!
 | 
			
		||||
//! This module provides components to perform event routing. The most important component for this
 | 
			
		||||
//! task is the [EventManager]. It receives all events and then routes them to event subscribers
 | 
			
		||||
//! where appropriate.
 | 
			
		||||
//!
 | 
			
		||||
//! The event manager has a listener table abstracted by the [ListenerMapProvider], which maps
 | 
			
		||||
//! listener groups identified by [ListenerKey]s to a [listener ID][ComponentId].
 | 
			
		||||
//! It also contains a sender table abstracted by the [SenderMapProvider] which maps these sender
 | 
			
		||||
//! IDs to concrete [EventSendProvider]s. A simple approach would be to use one send event provider
 | 
			
		||||
//! for each OBSW thread and then subscribe for all interesting events for a particular thread
 | 
			
		||||
//! using the send event provider ID.
 | 
			
		||||
//!
 | 
			
		||||
//! This can be done with the [EventManager] like this:
 | 
			
		||||
//!
 | 
			
		||||
//!  1. Provide a concrete [EventReceiveProvider] implementation. This abstraction allow to use different
 | 
			
		||||
//!     message queue backends. A straightforward implementation where dynamic memory allocation is
 | 
			
		||||
//!     not a big concern would be to use the [std::sync::mpsc::Receiver] handle. The trait is
 | 
			
		||||
//!     already implemented for this type.
 | 
			
		||||
//!  2. To set up event creators, create channel pairs using some message queue implementation.
 | 
			
		||||
//!     Each event creator gets a (cloned) sender component which allows it to send events to the
 | 
			
		||||
//!     manager.
 | 
			
		||||
//!  3. The event manager receives the receiver component as part of a [EventReceiveProvider]
 | 
			
		||||
//!     implementation so all events are routed to the manager.
 | 
			
		||||
//!  4. Create the [event sender map][SenderMapProvider]s which allow routing events to
 | 
			
		||||
//!     subscribers. You can now use the subscriber component IDs to subscribe
 | 
			
		||||
//!     for event groups, for example by using the [EventManager::subscribe_single] method.
 | 
			
		||||
//!  5. Add the send provider as well using the [EventManager::add_sender] call so the event
 | 
			
		||||
//!     manager can route listener groups to a the send provider.
 | 
			
		||||
//!
 | 
			
		||||
//! Some components like a PUS Event Service or PUS Event Action Service might require all
 | 
			
		||||
//! events to package them as telemetry or start actions where applicable.
 | 
			
		||||
//! Other components might only be interested in certain events. For example, a thermal system
 | 
			
		||||
//! handler might only be interested in temperature events generated by a thermal sensor component.
 | 
			
		||||
//!
 | 
			
		||||
//! # Examples
 | 
			
		||||
//!
 | 
			
		||||
//! You can check [integration test](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs/tests/pus_events.rs)
 | 
			
		||||
//! for a concrete example using multi-threading where events are routed to
 | 
			
		||||
//! different threads.
 | 
			
		||||
//!
 | 
			
		||||
//! The [satrs-example](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example)
 | 
			
		||||
//! also contains a full event manager instance and exposes a test event via the PUS test service.
 | 
			
		||||
//! The [PUS event](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/pus/event.rs)
 | 
			
		||||
//! module and the generic [events module](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/events.rs)
 | 
			
		||||
//! show how the event management modules can be integrated into a more complex software.
 | 
			
		||||
use crate::events_legacy::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw};
 | 
			
		||||
use crate::params::Params;
 | 
			
		||||
use crate::queue::GenericSendError;
 | 
			
		||||
use core::fmt::Debug;
 | 
			
		||||
use core::marker::PhantomData;
 | 
			
		||||
use core::slice::Iter;
 | 
			
		||||
 | 
			
		||||
use crate::ComponentId;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
pub use alloc_mod::*;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
pub use std_mod::*;
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
 | 
			
		||||
pub enum ListenerKey {
 | 
			
		||||
    Single(LargestEventRaw),
 | 
			
		||||
    Group(LargestGroupIdRaw),
 | 
			
		||||
    All,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct EventMessage<Event: GenericEvent, Parameters: Debug = Params> {
 | 
			
		||||
    sender_id: ComponentId,
 | 
			
		||||
    event: Event,
 | 
			
		||||
    params: Option<Parameters>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Event: GenericEvent, Parameters: Debug + Clone> EventMessage<Event, Parameters> {
 | 
			
		||||
    pub fn new_generic(sender_id: ComponentId, event: Event, params: Option<&Parameters>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            sender_id,
 | 
			
		||||
            event,
 | 
			
		||||
            params: params.cloned(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sender_id(&self) -> ComponentId {
 | 
			
		||||
        self.sender_id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn event(&self) -> Event {
 | 
			
		||||
        self.event
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn params(&self) -> Option<&Parameters> {
 | 
			
		||||
        self.params.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new(sender_id: ComponentId, event: Event) -> Self {
 | 
			
		||||
        Self::new_generic(sender_id, event, None)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new_with_params(sender_id: ComponentId, event: Event, params: &Parameters) -> Self {
 | 
			
		||||
        Self::new_generic(sender_id, event, Some(params))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type EventMessageU32 = EventMessage<EventU32, Params>;
 | 
			
		||||
pub type EventMessageU16 = EventMessage<EventU16, Params>;
 | 
			
		||||
 | 
			
		||||
/// Generic abstraction
 | 
			
		||||
pub trait EventSendProvider<Event: GenericEvent, ParamProvider: Debug = Params> {
 | 
			
		||||
    type Error;
 | 
			
		||||
 | 
			
		||||
    fn target_id(&self) -> ComponentId;
 | 
			
		||||
 | 
			
		||||
    fn send(&self, message: EventMessage<Event, ParamProvider>) -> Result<(), Self::Error>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generic abstraction for an event receiver.
 | 
			
		||||
pub trait EventReceiveProvider<Event: GenericEvent, ParamsProvider: Debug = Params> {
 | 
			
		||||
    type Error;
 | 
			
		||||
 | 
			
		||||
    /// This function has to be provided by any event receiver. A call may or may not return
 | 
			
		||||
    /// an event and optional auxiliary data.
 | 
			
		||||
    fn try_recv_event(&self) -> Result<Option<EventMessage<Event, ParamsProvider>>, Self::Error>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ListenerMapProvider {
 | 
			
		||||
    #[cfg(feature = "alloc")]
 | 
			
		||||
    fn get_listeners(&self) -> alloc::vec::Vec<ListenerKey>;
 | 
			
		||||
    fn contains_listener(&self, key: &ListenerKey) -> bool;
 | 
			
		||||
    fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<'_, ComponentId>>;
 | 
			
		||||
    fn add_listener(&mut self, key: ListenerKey, listener_id: ComponentId) -> bool;
 | 
			
		||||
    fn remove_duplicates(&mut self, key: &ListenerKey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait SenderMapProvider<
 | 
			
		||||
    EventSender: EventSendProvider<Event, ParamProvider>,
 | 
			
		||||
    Event: GenericEvent = EventU32,
 | 
			
		||||
    ParamProvider: Debug = Params,
 | 
			
		||||
>
 | 
			
		||||
{
 | 
			
		||||
    fn contains_send_event_provider(&self, target_id: &ComponentId) -> bool;
 | 
			
		||||
 | 
			
		||||
    fn get_send_event_provider(&self, target_id: &ComponentId) -> Option<&EventSender>;
 | 
			
		||||
    fn add_send_event_provider(&mut self, send_provider: EventSender) -> bool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generic event manager implementation.
 | 
			
		||||
///
 | 
			
		||||
/// # Generics
 | 
			
		||||
///
 | 
			
		||||
///  * `EventReceiver`: [EventReceiveProvider] used to receive all events.
 | 
			
		||||
///  * `SenderMap`: [SenderMapProvider]  which maps channel IDs to send providers.
 | 
			
		||||
///  * `ListenerMap`: [ListenerMapProvider] which maps listener keys to channel IDs.
 | 
			
		||||
///  * `EventSender`: [EventSendProvider] contained within the sender map which sends the events.
 | 
			
		||||
///  * `Event`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32]
 | 
			
		||||
///    and [EventU16] are supported.
 | 
			
		||||
///  * `ParamProvider`: Auxiliary data which is sent with the event to provide optional context
 | 
			
		||||
///    information
 | 
			
		||||
pub struct EventManager<
 | 
			
		||||
    EventReceiver: EventReceiveProvider<Event, ParamProvider>,
 | 
			
		||||
    SenderMap: SenderMapProvider<EventSender, Event, ParamProvider>,
 | 
			
		||||
    ListenerMap: ListenerMapProvider,
 | 
			
		||||
    EventSender: EventSendProvider<Event, ParamProvider>,
 | 
			
		||||
    Event: GenericEvent = EventU32,
 | 
			
		||||
    ParamProvider: Debug = Params,
 | 
			
		||||
> {
 | 
			
		||||
    event_receiver: EventReceiver,
 | 
			
		||||
    sender_map: SenderMap,
 | 
			
		||||
    listener_map: ListenerMap,
 | 
			
		||||
    phantom: core::marker::PhantomData<(EventSender, Event, ParamProvider)>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum EventRoutingResult<Event: GenericEvent, ParamProvider: Debug> {
 | 
			
		||||
    /// No event was received
 | 
			
		||||
    Empty,
 | 
			
		||||
    /// An event was received and routed to listeners.
 | 
			
		||||
    Handled {
 | 
			
		||||
        num_recipients: u32,
 | 
			
		||||
        event_msg: EventMessage<Event, ParamProvider>,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum EventRoutingError {
 | 
			
		||||
    Send(GenericSendError),
 | 
			
		||||
    NoSendersForKey(ListenerKey),
 | 
			
		||||
    NoSenderForId(ComponentId),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<
 | 
			
		||||
    EventReceiver: EventReceiveProvider<Event, ParamProvider>,
 | 
			
		||||
    SenderMap: SenderMapProvider<EventSender, Event, ParamProvider>,
 | 
			
		||||
    ListenerMap: ListenerMapProvider,
 | 
			
		||||
    EventSender: EventSendProvider<Event, ParamProvider>,
 | 
			
		||||
    Event: GenericEvent + Copy,
 | 
			
		||||
    ParamProvider: Debug,
 | 
			
		||||
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSender, Event, ParamProvider>
 | 
			
		||||
{
 | 
			
		||||
    pub fn remove_duplicates(&mut self, key: &ListenerKey) {
 | 
			
		||||
        self.listener_map.remove_duplicates(key)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Subscribe for a unique event.
 | 
			
		||||
    pub fn subscribe_single(&mut self, event: &Event, sender_id: ComponentId) {
 | 
			
		||||
        self.update_listeners(ListenerKey::Single(event.raw_as_largest_type()), sender_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Subscribe for an event group.
 | 
			
		||||
    pub fn subscribe_group(&mut self, group_id: LargestGroupIdRaw, sender_id: ComponentId) {
 | 
			
		||||
        self.update_listeners(ListenerKey::Group(group_id), sender_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Subscribe for all events received by the manager.
 | 
			
		||||
    ///
 | 
			
		||||
    /// For example, this can be useful for a handler component which sends every event as
 | 
			
		||||
    /// a telemetry packet.
 | 
			
		||||
    pub fn subscribe_all(&mut self, sender_id: ComponentId) {
 | 
			
		||||
        self.update_listeners(ListenerKey::All, sender_id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl<
 | 
			
		||||
    EventReceiver: EventReceiveProvider<Event, ParamProvider>,
 | 
			
		||||
    SenderMap: SenderMapProvider<EventSenderMap, Event, ParamProvider>,
 | 
			
		||||
    ListenerMap: ListenerMapProvider,
 | 
			
		||||
    EventSenderMap: EventSendProvider<Event, ParamProvider>,
 | 
			
		||||
    Event: GenericEvent + Copy,
 | 
			
		||||
    ParamProvider: Debug,
 | 
			
		||||
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSenderMap, Event, ParamProvider>
 | 
			
		||||
{
 | 
			
		||||
    pub fn new_with_custom_maps(
 | 
			
		||||
        event_receiver: EventReceiver,
 | 
			
		||||
        sender_map: SenderMap,
 | 
			
		||||
        listener_map: ListenerMap,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        EventManager {
 | 
			
		||||
            listener_map,
 | 
			
		||||
            sender_map,
 | 
			
		||||
            event_receiver,
 | 
			
		||||
            phantom: PhantomData,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Add a new sender component which can be used to send events to subscribers.
 | 
			
		||||
    pub fn add_sender(&mut self, send_provider: EventSenderMap) {
 | 
			
		||||
        if !self
 | 
			
		||||
            .sender_map
 | 
			
		||||
            .contains_send_event_provider(&send_provider.target_id())
 | 
			
		||||
        {
 | 
			
		||||
            self.sender_map.add_send_event_provider(send_provider);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Generic function to update the event subscribers.
 | 
			
		||||
    fn update_listeners(&mut self, key: ListenerKey, sender_id: ComponentId) {
 | 
			
		||||
        self.listener_map.add_listener(key, sender_id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<
 | 
			
		||||
    EventReceiver: EventReceiveProvider<Event, ParamProvider>,
 | 
			
		||||
    SenderMap: SenderMapProvider<EventSenderMap, Event, ParamProvider>,
 | 
			
		||||
    ListenerMap: ListenerMapProvider,
 | 
			
		||||
    EventSenderMap: EventSendProvider<Event, ParamProvider, Error = GenericSendError>,
 | 
			
		||||
    Event: GenericEvent + Copy,
 | 
			
		||||
    ParamProvider: Clone + Debug,
 | 
			
		||||
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSenderMap, Event, ParamProvider>
 | 
			
		||||
{
 | 
			
		||||
    /// This function will use the cached event receiver and try to receive one event.
 | 
			
		||||
    /// If an event was received, it will try to route that event to all subscribed event listeners.
 | 
			
		||||
    /// If this works without any issues, the [EventRoutingResult] will contain context information
 | 
			
		||||
    /// about the routed event.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If an error occurs during the routing, the error handler will be called. The error handler
 | 
			
		||||
    /// should take a reference to the event message as the first argument, and the routing error
 | 
			
		||||
    /// as the second argument.
 | 
			
		||||
    pub fn try_event_handling<E: FnMut(&EventMessage<Event, ParamProvider>, EventRoutingError)>(
 | 
			
		||||
        &self,
 | 
			
		||||
        mut error_handler: E,
 | 
			
		||||
    ) -> EventRoutingResult<Event, ParamProvider> {
 | 
			
		||||
        let mut num_recipients = 0;
 | 
			
		||||
        let mut send_handler =
 | 
			
		||||
            |key: &ListenerKey, event_msg: &EventMessage<Event, ParamProvider>| {
 | 
			
		||||
                if self.listener_map.contains_listener(key) {
 | 
			
		||||
                    if let Some(ids) = self.listener_map.get_listener_ids(key) {
 | 
			
		||||
                        for id in ids {
 | 
			
		||||
                            if let Some(sender) = self.sender_map.get_send_event_provider(id) {
 | 
			
		||||
                                if let Err(e) = sender.send(EventMessage::new_generic(
 | 
			
		||||
                                    event_msg.sender_id,
 | 
			
		||||
                                    event_msg.event,
 | 
			
		||||
                                    event_msg.params.as_ref(),
 | 
			
		||||
                                )) {
 | 
			
		||||
                                    error_handler(event_msg, EventRoutingError::Send(e));
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    num_recipients += 1;
 | 
			
		||||
                                }
 | 
			
		||||
                            } else {
 | 
			
		||||
                                error_handler(event_msg, EventRoutingError::NoSenderForId(*id));
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        error_handler(event_msg, EventRoutingError::NoSendersForKey(*key));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        if let Ok(Some(event_msg)) = self.event_receiver.try_recv_event() {
 | 
			
		||||
            let single_key = ListenerKey::Single(event_msg.event.raw_as_largest_type());
 | 
			
		||||
            send_handler(&single_key, &event_msg);
 | 
			
		||||
            let group_key = ListenerKey::Group(event_msg.event.group_id_as_largest_type());
 | 
			
		||||
            send_handler(&group_key, &event_msg);
 | 
			
		||||
            send_handler(&ListenerKey::All, &event_msg);
 | 
			
		||||
            return EventRoutingResult::Handled {
 | 
			
		||||
                num_recipients,
 | 
			
		||||
                event_msg,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        EventRoutingResult::Empty
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
pub mod alloc_mod {
 | 
			
		||||
    use alloc::vec::Vec;
 | 
			
		||||
    use hashbrown::HashMap;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    /// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap]
 | 
			
		||||
    /// and the [DefaultListenerMap]. It uses regular mpsc channels as the message queue backend.
 | 
			
		||||
    pub type EventManagerWithMpsc<Event = EventU32, ParamProvider = Params> = EventManager<
 | 
			
		||||
        EventU32ReceiverMpsc<ParamProvider>,
 | 
			
		||||
        DefaultSenderMap<EventSenderMpsc<Event>, Event, ParamProvider>,
 | 
			
		||||
        DefaultListenerMap,
 | 
			
		||||
        EventSenderMpsc<Event>,
 | 
			
		||||
    >;
 | 
			
		||||
 | 
			
		||||
    /// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap]
 | 
			
		||||
    /// and the [DefaultListenerMap]. It uses
 | 
			
		||||
    /// [bounded mpsc senders](https://doc.rust-lang.org/std/sync/mpsc/struct.SyncSender.html) as the
 | 
			
		||||
    /// message queue backend.
 | 
			
		||||
    pub type EventManagerWithBoundedMpsc<Event = EventU32, ParamProvider = Params> = EventManager<
 | 
			
		||||
        EventU32ReceiverMpsc<ParamProvider>,
 | 
			
		||||
        DefaultSenderMap<EventSenderMpscBounded<Event>, Event, ParamProvider>,
 | 
			
		||||
        DefaultListenerMap,
 | 
			
		||||
        EventSenderMpscBounded<Event>,
 | 
			
		||||
    >;
 | 
			
		||||
 | 
			
		||||
    impl<
 | 
			
		||||
        EventReceiver: EventReceiveProvider<Event, ParamProvider>,
 | 
			
		||||
        EventSender: EventSendProvider<Event, ParamProvider>,
 | 
			
		||||
        Event: GenericEvent + Copy,
 | 
			
		||||
        ParamProvider: 'static + Debug,
 | 
			
		||||
    >
 | 
			
		||||
        EventManager<
 | 
			
		||||
            EventReceiver,
 | 
			
		||||
            DefaultSenderMap<EventSender, Event, ParamProvider>,
 | 
			
		||||
            DefaultListenerMap,
 | 
			
		||||
            EventSender,
 | 
			
		||||
            Event,
 | 
			
		||||
            ParamProvider,
 | 
			
		||||
        >
 | 
			
		||||
    {
 | 
			
		||||
        /// Create an event manager where the sender table will be the [DefaultSenderMap]
 | 
			
		||||
        /// and the listener table will be the [DefaultListenerMap].
 | 
			
		||||
        pub fn new(event_receiver: EventReceiver) -> Self {
 | 
			
		||||
            Self {
 | 
			
		||||
                listener_map: DefaultListenerMap::default(),
 | 
			
		||||
                sender_map: DefaultSenderMap::default(),
 | 
			
		||||
                event_receiver,
 | 
			
		||||
                phantom: PhantomData,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Default listener map.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Simple implementation which uses a [HashMap] and a [Vec] internally.
 | 
			
		||||
    #[derive(Default)]
 | 
			
		||||
    pub struct DefaultListenerMap {
 | 
			
		||||
        listeners: HashMap<ListenerKey, Vec<ComponentId>>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl ListenerMapProvider for DefaultListenerMap {
 | 
			
		||||
        fn get_listeners(&self) -> Vec<ListenerKey> {
 | 
			
		||||
            let mut key_list = Vec::new();
 | 
			
		||||
            for key in self.listeners.keys() {
 | 
			
		||||
                key_list.push(*key);
 | 
			
		||||
            }
 | 
			
		||||
            key_list
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn contains_listener(&self, key: &ListenerKey) -> bool {
 | 
			
		||||
            self.listeners.contains_key(key)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<'_, ComponentId>> {
 | 
			
		||||
            self.listeners.get(key).map(|vec| vec.iter())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn add_listener(&mut self, key: ListenerKey, sender_id: ComponentId) -> bool {
 | 
			
		||||
            if let Some(existing_list) = self.listeners.get_mut(&key) {
 | 
			
		||||
                existing_list.push(sender_id);
 | 
			
		||||
            } else {
 | 
			
		||||
                let new_list = alloc::vec![sender_id];
 | 
			
		||||
                self.listeners.insert(key, new_list);
 | 
			
		||||
            }
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn remove_duplicates(&mut self, key: &ListenerKey) {
 | 
			
		||||
            if let Some(list) = self.listeners.get_mut(key) {
 | 
			
		||||
                list.sort_unstable();
 | 
			
		||||
                list.dedup();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Default sender map.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Simple implementation which uses a [HashMap] internally.
 | 
			
		||||
    pub struct DefaultSenderMap<
 | 
			
		||||
        EventSender: EventSendProvider<Event, ParamProvider>,
 | 
			
		||||
        Event: GenericEvent = EventU32,
 | 
			
		||||
        ParamProvider: Debug = Params,
 | 
			
		||||
    > {
 | 
			
		||||
        senders: HashMap<ComponentId, EventSender>,
 | 
			
		||||
        phantom: PhantomData<(Event, ParamProvider)>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<
 | 
			
		||||
        EventSender: EventSendProvider<Event, ParamProvider>,
 | 
			
		||||
        Event: GenericEvent,
 | 
			
		||||
        ParamProvider: Debug,
 | 
			
		||||
    > Default for DefaultSenderMap<EventSender, Event, ParamProvider>
 | 
			
		||||
    {
 | 
			
		||||
        fn default() -> Self {
 | 
			
		||||
            Self {
 | 
			
		||||
                senders: Default::default(),
 | 
			
		||||
                phantom: Default::default(),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<
 | 
			
		||||
        EventSender: EventSendProvider<Event, ParamProvider>,
 | 
			
		||||
        Event: GenericEvent,
 | 
			
		||||
        ParamProvider: Debug,
 | 
			
		||||
    > SenderMapProvider<EventSender, Event, ParamProvider>
 | 
			
		||||
        for DefaultSenderMap<EventSender, Event, ParamProvider>
 | 
			
		||||
    {
 | 
			
		||||
        fn contains_send_event_provider(&self, id: &ComponentId) -> bool {
 | 
			
		||||
            self.senders.contains_key(id)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn get_send_event_provider(&self, id: &ComponentId) -> Option<&EventSender> {
 | 
			
		||||
            self.senders
 | 
			
		||||
                .get(id)
 | 
			
		||||
                .filter(|sender| sender.target_id() == *id)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn add_send_event_provider(&mut self, send_provider: EventSender) -> bool {
 | 
			
		||||
            let id = send_provider.target_id();
 | 
			
		||||
            if self.senders.contains_key(&id) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            self.senders.insert(id, send_provider).is_none()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
pub mod std_mod {
 | 
			
		||||
    use crate::queue::GenericReceiveError;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use std::sync::mpsc;
 | 
			
		||||
 | 
			
		||||
    impl<Event: GenericEvent + Send, ParamProvider: Debug>
 | 
			
		||||
        EventReceiveProvider<Event, ParamProvider>
 | 
			
		||||
        for mpsc::Receiver<EventMessage<Event, ParamProvider>>
 | 
			
		||||
    {
 | 
			
		||||
        type Error = GenericReceiveError;
 | 
			
		||||
 | 
			
		||||
        fn try_recv_event(
 | 
			
		||||
            &self,
 | 
			
		||||
        ) -> Result<Option<EventMessage<Event, ParamProvider>>, Self::Error> {
 | 
			
		||||
            match self.try_recv() {
 | 
			
		||||
                Ok(msg) => Ok(Some(msg)),
 | 
			
		||||
                Err(e) => match e {
 | 
			
		||||
                    mpsc::TryRecvError::Empty => Ok(None),
 | 
			
		||||
                    mpsc::TryRecvError::Disconnected => {
 | 
			
		||||
                        Err(GenericReceiveError::TxDisconnected(None))
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub type EventU32ReceiverMpsc<ParamProvider = Params> =
 | 
			
		||||
        mpsc::Receiver<EventMessage<EventU32, ParamProvider>>;
 | 
			
		||||
    pub type EventU16ReceiverMpsc<ParamProvider = Params> =
 | 
			
		||||
        mpsc::Receiver<EventMessage<EventU16, ParamProvider>>;
 | 
			
		||||
 | 
			
		||||
    /// Generic event sender which uses a regular [mpsc::Sender] as the messaging backend to
 | 
			
		||||
    /// send events.
 | 
			
		||||
    #[derive(Clone)]
 | 
			
		||||
    pub struct EventSenderMpsc<Event: GenericEvent + Send> {
 | 
			
		||||
        target_id: ComponentId,
 | 
			
		||||
        sender: mpsc::Sender<EventMessage<Event>>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<Event: GenericEvent + Send> EventSenderMpsc<Event> {
 | 
			
		||||
        pub fn new(target_id: ComponentId, sender: mpsc::Sender<EventMessage<Event>>) -> Self {
 | 
			
		||||
            Self { target_id, sender }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<Event: GenericEvent + Send> EventSendProvider<Event> for EventSenderMpsc<Event> {
 | 
			
		||||
        type Error = GenericSendError;
 | 
			
		||||
 | 
			
		||||
        fn target_id(&self) -> ComponentId {
 | 
			
		||||
            self.target_id
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn send(&self, event_msg: EventMessage<Event>) -> Result<(), GenericSendError> {
 | 
			
		||||
            self.sender
 | 
			
		||||
                .send(event_msg)
 | 
			
		||||
                .map_err(|_| GenericSendError::RxDisconnected)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Generic event sender which uses the [mpsc::SyncSender] as the messaging backend to send
 | 
			
		||||
    /// events. This has the advantage that the channel is bounded and thus more deterministic.
 | 
			
		||||
    #[derive(Clone)]
 | 
			
		||||
    pub struct EventSenderMpscBounded<Event: GenericEvent + Send> {
 | 
			
		||||
        target_id: ComponentId,
 | 
			
		||||
        sender: mpsc::SyncSender<EventMessage<Event>>,
 | 
			
		||||
        capacity: usize,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<Event: GenericEvent + Send> EventSenderMpscBounded<Event> {
 | 
			
		||||
        pub fn new(
 | 
			
		||||
            target_id: ComponentId,
 | 
			
		||||
            sender: mpsc::SyncSender<EventMessage<Event>>,
 | 
			
		||||
            capacity: usize,
 | 
			
		||||
        ) -> Self {
 | 
			
		||||
            Self {
 | 
			
		||||
                target_id,
 | 
			
		||||
                sender,
 | 
			
		||||
                capacity,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<Event: GenericEvent + Send> EventSendProvider<Event> for EventSenderMpscBounded<Event> {
 | 
			
		||||
        type Error = GenericSendError;
 | 
			
		||||
 | 
			
		||||
        fn target_id(&self) -> ComponentId {
 | 
			
		||||
            self.target_id
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn send(&self, event_msg: EventMessage<Event>) -> Result<(), Self::Error> {
 | 
			
		||||
            if let Err(e) = self.sender.try_send(event_msg) {
 | 
			
		||||
                return match e {
 | 
			
		||||
                    mpsc::TrySendError::Full(_) => {
 | 
			
		||||
                        Err(GenericSendError::QueueFull(Some(self.capacity as u32)))
 | 
			
		||||
                    }
 | 
			
		||||
                    mpsc::TrySendError::Disconnected(_) => Err(GenericSendError::RxDisconnected),
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub type EventU32SenderMpsc = EventSenderMpsc<EventU32>;
 | 
			
		||||
    pub type EventU16SenderMpsc = EventSenderMpsc<EventU16>;
 | 
			
		||||
    pub type EventU32SenderMpscBounded = EventSenderMpscBounded<EventU32>;
 | 
			
		||||
    pub type EventU16SenderMpscBounded = EventSenderMpscBounded<EventU16>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::event_man_legacy::EventManager;
 | 
			
		||||
    use crate::events_legacy::{EventU32, GenericEvent, Severity};
 | 
			
		||||
    use crate::params::{ParamsHeapless, ParamsRaw};
 | 
			
		||||
    use crate::pus::test_util::{TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1};
 | 
			
		||||
    use std::format;
 | 
			
		||||
    use std::sync::mpsc::{self};
 | 
			
		||||
 | 
			
		||||
    const TEST_EVENT: EventU32 = EventU32::new(Severity::Info, 0, 5);
 | 
			
		||||
 | 
			
		||||
    fn check_next_event(
 | 
			
		||||
        expected: EventU32,
 | 
			
		||||
        receiver: &mpsc::Receiver<EventMessageU32>,
 | 
			
		||||
    ) -> Option<Params> {
 | 
			
		||||
        if let Ok(event_msg) = receiver.try_recv() {
 | 
			
		||||
            assert_eq!(event_msg.event, expected);
 | 
			
		||||
            return event_msg.params;
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn check_handled_event(
 | 
			
		||||
        res: EventRoutingResult<EventU32, Params>,
 | 
			
		||||
        expected: EventU32,
 | 
			
		||||
        expected_num_sent: u32,
 | 
			
		||||
        expected_sender_id: ComponentId,
 | 
			
		||||
    ) {
 | 
			
		||||
        assert!(matches!(res, EventRoutingResult::Handled { .. }));
 | 
			
		||||
        if let EventRoutingResult::Handled {
 | 
			
		||||
            num_recipients,
 | 
			
		||||
            event_msg,
 | 
			
		||||
        } = res
 | 
			
		||||
        {
 | 
			
		||||
            assert_eq!(event_msg.event, expected);
 | 
			
		||||
            assert_eq!(event_msg.sender_id, expected_sender_id);
 | 
			
		||||
            assert_eq!(num_recipients, expected_num_sent);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn generic_event_man() -> (mpsc::Sender<EventMessageU32>, EventManagerWithMpsc) {
 | 
			
		||||
        let (event_sender, event_receiver) = mpsc::channel();
 | 
			
		||||
        (event_sender, EventManager::new(event_receiver))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_basic() {
 | 
			
		||||
        let (event_sender, mut event_man) = generic_event_man();
 | 
			
		||||
        let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
 | 
			
		||||
        let event_grp_1_0 = EventU32::new(Severity::High, 1, 0);
 | 
			
		||||
        let (single_event_sender, single_event_receiver) = mpsc::channel();
 | 
			
		||||
        let single_event_listener = EventSenderMpsc::new(0, single_event_sender);
 | 
			
		||||
        event_man.subscribe_single(&event_grp_0, single_event_listener.target_id());
 | 
			
		||||
        event_man.add_sender(single_event_listener);
 | 
			
		||||
        let (group_event_sender_0, group_event_receiver_0) = mpsc::channel();
 | 
			
		||||
        let group_event_listener = EventU32SenderMpsc::new(1, group_event_sender_0);
 | 
			
		||||
        event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener.target_id());
 | 
			
		||||
        event_man.add_sender(group_event_listener);
 | 
			
		||||
 | 
			
		||||
        let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
 | 
			
		||||
            panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
 | 
			
		||||
        };
 | 
			
		||||
        // Test event with one listener
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0))
 | 
			
		||||
            .expect("Sending single error failed");
 | 
			
		||||
        let res = event_man.try_event_handling(&error_handler);
 | 
			
		||||
        check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
 | 
			
		||||
        check_next_event(event_grp_0, &single_event_receiver);
 | 
			
		||||
 | 
			
		||||
        // Test event which is sent to all group listeners
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0))
 | 
			
		||||
            .expect("Sending group error failed");
 | 
			
		||||
        let res = event_man.try_event_handling(&error_handler);
 | 
			
		||||
        check_handled_event(res, event_grp_1_0, 1, TEST_COMPONENT_ID_1.id());
 | 
			
		||||
        check_next_event(event_grp_1_0, &group_event_receiver_0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_with_basic_params() {
 | 
			
		||||
        let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
 | 
			
		||||
            panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
 | 
			
		||||
        };
 | 
			
		||||
        let (event_sender, mut event_man) = generic_event_man();
 | 
			
		||||
        let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
 | 
			
		||||
        let (single_event_sender, single_event_receiver) = mpsc::channel();
 | 
			
		||||
        let single_event_listener = EventSenderMpsc::new(0, single_event_sender);
 | 
			
		||||
        event_man.subscribe_single(&event_grp_0, single_event_listener.target_id());
 | 
			
		||||
        event_man.add_sender(single_event_listener);
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new_with_params(
 | 
			
		||||
                TEST_COMPONENT_ID_0.id(),
 | 
			
		||||
                event_grp_0,
 | 
			
		||||
                &Params::Heapless((2_u32, 3_u32).into()),
 | 
			
		||||
            ))
 | 
			
		||||
            .expect("Sending group error failed");
 | 
			
		||||
        let res = event_man.try_event_handling(&error_handler);
 | 
			
		||||
        check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
 | 
			
		||||
        let aux = check_next_event(event_grp_0, &single_event_receiver);
 | 
			
		||||
        assert!(aux.is_some());
 | 
			
		||||
        let aux = aux.unwrap();
 | 
			
		||||
        if let Params::Heapless(ParamsHeapless::Raw(ParamsRaw::U32Pair(pair))) = aux {
 | 
			
		||||
            assert_eq!(pair.0, 2);
 | 
			
		||||
            assert_eq!(pair.1, 3);
 | 
			
		||||
        } else {
 | 
			
		||||
            panic!("{}", format!("Unexpected auxiliary value type {:?}", aux));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Test listening for multiple groups
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_multi_group() {
 | 
			
		||||
        let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
 | 
			
		||||
            panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
 | 
			
		||||
        };
 | 
			
		||||
        let (event_sender, mut event_man) = generic_event_man();
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        assert!(matches!(res, EventRoutingResult::Empty));
 | 
			
		||||
 | 
			
		||||
        let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
 | 
			
		||||
        let event_grp_1_0 = EventU32::new(Severity::High, 1, 0);
 | 
			
		||||
        let (event_grp_0_sender, event_grp_0_receiver) = mpsc::channel();
 | 
			
		||||
        let event_grp_0_and_1_listener = EventU32SenderMpsc::new(0, event_grp_0_sender);
 | 
			
		||||
        event_man.subscribe_group(
 | 
			
		||||
            event_grp_0.group_id(),
 | 
			
		||||
            event_grp_0_and_1_listener.target_id(),
 | 
			
		||||
        );
 | 
			
		||||
        event_man.subscribe_group(
 | 
			
		||||
            event_grp_1_0.group_id(),
 | 
			
		||||
            event_grp_0_and_1_listener.target_id(),
 | 
			
		||||
        );
 | 
			
		||||
        event_man.add_sender(event_grp_0_and_1_listener);
 | 
			
		||||
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0))
 | 
			
		||||
            .expect("Sending Event Group 0 failed");
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0))
 | 
			
		||||
            .expect("Sendign Event Group 1 failed");
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        check_handled_event(res, event_grp_1_0, 1, TEST_COMPONENT_ID_1.id());
 | 
			
		||||
 | 
			
		||||
        check_next_event(event_grp_0, &event_grp_0_receiver);
 | 
			
		||||
        check_next_event(event_grp_1_0, &event_grp_0_receiver);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Test listening to the same event from multiple listeners. Also test listening
 | 
			
		||||
    /// to both group and single events from one listener
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_listening_to_same_event_and_multi_type() {
 | 
			
		||||
        let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
 | 
			
		||||
            panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
 | 
			
		||||
        };
 | 
			
		||||
        let (event_sender, mut event_man) = generic_event_man();
 | 
			
		||||
        let event_0 = EventU32::new(Severity::Info, 0, 5);
 | 
			
		||||
        let event_1 = EventU32::new(Severity::High, 1, 0);
 | 
			
		||||
        let (event_0_tx_0, event_0_rx_0) = mpsc::channel();
 | 
			
		||||
        let (event_0_tx_1, event_0_rx_1) = mpsc::channel();
 | 
			
		||||
        let event_listener_0 = EventU32SenderMpsc::new(0, event_0_tx_0);
 | 
			
		||||
        let event_listener_1 = EventU32SenderMpsc::new(1, event_0_tx_1);
 | 
			
		||||
        let event_listener_0_sender_id = event_listener_0.target_id();
 | 
			
		||||
        event_man.subscribe_single(&event_0, event_listener_0_sender_id);
 | 
			
		||||
        event_man.add_sender(event_listener_0);
 | 
			
		||||
        let event_listener_1_sender_id = event_listener_1.target_id();
 | 
			
		||||
        event_man.subscribe_single(&event_0, event_listener_1_sender_id);
 | 
			
		||||
        event_man.add_sender(event_listener_1);
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
 | 
			
		||||
            .expect("Triggering Event 0 failed");
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        check_handled_event(res, event_0, 2, TEST_COMPONENT_ID_0.id());
 | 
			
		||||
        check_next_event(event_0, &event_0_rx_0);
 | 
			
		||||
        check_next_event(event_0, &event_0_rx_1);
 | 
			
		||||
        event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
 | 
			
		||||
            .expect("Triggering Event 0 failed");
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1))
 | 
			
		||||
            .expect("Triggering Event 1 failed");
 | 
			
		||||
 | 
			
		||||
        // 3 Events messages will be sent now
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        check_handled_event(res, event_0, 2, TEST_COMPONENT_ID_0.id());
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_1.id());
 | 
			
		||||
        // Both the single event and the group event should arrive now
 | 
			
		||||
        check_next_event(event_0, &event_0_rx_0);
 | 
			
		||||
        check_next_event(event_1, &event_0_rx_0);
 | 
			
		||||
 | 
			
		||||
        // Do double insertion and then remove duplicates
 | 
			
		||||
        event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
 | 
			
		||||
        event_man.remove_duplicates(&ListenerKey::Group(event_1.group_id()));
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_1))
 | 
			
		||||
            .expect("Triggering Event 1 failed");
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_0.id());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_all_events_listener() {
 | 
			
		||||
        let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
 | 
			
		||||
            panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
 | 
			
		||||
        };
 | 
			
		||||
        let (event_sender, event_receiver) = mpsc::channel();
 | 
			
		||||
        let mut event_man = EventManagerWithMpsc::new(event_receiver);
 | 
			
		||||
        let event_0 = EventU32::new(Severity::Info, 0, 5);
 | 
			
		||||
        let event_1 = EventU32::new(Severity::High, 1, 0);
 | 
			
		||||
        let (event_0_tx_0, all_events_rx) = mpsc::channel();
 | 
			
		||||
        let all_events_listener = EventU32SenderMpsc::new(0, event_0_tx_0);
 | 
			
		||||
        event_man.subscribe_all(all_events_listener.target_id());
 | 
			
		||||
        event_man.add_sender(all_events_listener);
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
 | 
			
		||||
            .expect("Triggering event 0 failed");
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1))
 | 
			
		||||
            .expect("Triggering event 1 failed");
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        check_handled_event(res, event_0, 1, TEST_COMPONENT_ID_0.id());
 | 
			
		||||
        let res = event_man.try_event_handling(error_handler);
 | 
			
		||||
        check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_1.id());
 | 
			
		||||
        check_next_event(event_0, &all_events_rx);
 | 
			
		||||
        check_next_event(event_1, &all_events_rx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_bounded_event_sender_queue_full() {
 | 
			
		||||
        let (event_sender, _event_receiver) = mpsc::sync_channel(3);
 | 
			
		||||
        let event_sender = EventU32SenderMpscBounded::new(1, event_sender, 3);
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
 | 
			
		||||
            .expect("sending test event failed");
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
 | 
			
		||||
            .expect("sending test event failed");
 | 
			
		||||
        event_sender
 | 
			
		||||
            .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
 | 
			
		||||
            .expect("sending test event failed");
 | 
			
		||||
        let error = event_sender.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT));
 | 
			
		||||
        if let Err(e) = error {
 | 
			
		||||
            assert!(matches!(e, GenericSendError::QueueFull(Some(3))));
 | 
			
		||||
        } else {
 | 
			
		||||
            panic!("unexpected error {error:?}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_bounded_event_sender_rx_dropped() {
 | 
			
		||||
        let (event_sender, event_receiver) = mpsc::sync_channel(3);
 | 
			
		||||
        let event_sender = EventU32SenderMpscBounded::new(1, event_sender, 3);
 | 
			
		||||
        drop(event_receiver);
 | 
			
		||||
        if let Err(e) = event_sender.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT)) {
 | 
			
		||||
            assert!(matches!(e, GenericSendError::RxDisconnected));
 | 
			
		||||
        } else {
 | 
			
		||||
            panic!("Expected error");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,859 +0,0 @@
 | 
			
		||||
//! # Event support module
 | 
			
		||||
//!
 | 
			
		||||
//! This is a legacy module. It is recommended to use [super::events] instead.
 | 
			
		||||
//!
 | 
			
		||||
//! This module includes the basic event structs [EventU32] and [EventU16] and versions with the
 | 
			
		||||
//! ECSS severity levels as a type parameter. These structs are simple abstractions on top of the
 | 
			
		||||
//! [u32] and [u16] types where the raw value is the unique identifier for a particular event.
 | 
			
		||||
//! The abstraction also allows to group related events using a group ID, and the severity
 | 
			
		||||
//! of an event is encoded inside the raw value itself with four possible [Severity] levels:
 | 
			
		||||
//!
 | 
			
		||||
//!  - INFO
 | 
			
		||||
//!  - LOW
 | 
			
		||||
//!  - MEDIUM
 | 
			
		||||
//!  - HIGH
 | 
			
		||||
//!
 | 
			
		||||
//! All event structs implement the [EcssEnumeration] trait and can be created as constants.
 | 
			
		||||
//! This allows to easily create a static list of constant events which can then be used to generate
 | 
			
		||||
//! event telemetry using the PUS event manager modules.
 | 
			
		||||
//!
 | 
			
		||||
//! # Examples
 | 
			
		||||
//!
 | 
			
		||||
//! ```
 | 
			
		||||
//! use satrs::events_legacy::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo};
 | 
			
		||||
//!
 | 
			
		||||
//! const MSG_RECVD: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(1, 0);
 | 
			
		||||
//! const MSG_FAILED: EventU32 = EventU32::new(Severity::Low, 1, 1);
 | 
			
		||||
//!
 | 
			
		||||
//! const TEMPERATURE_HIGH: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::new(2, 0);
 | 
			
		||||
//!
 | 
			
		||||
//! let small_event = EventU16::new(Severity::Info, 3, 0);
 | 
			
		||||
//! ```
 | 
			
		||||
use core::fmt::Debug;
 | 
			
		||||
use core::hash::Hash;
 | 
			
		||||
use core::marker::PhantomData;
 | 
			
		||||
use delegate::delegate;
 | 
			
		||||
use spacepackets::ByteConversionError;
 | 
			
		||||
use spacepackets::ecss::EcssEnumeration;
 | 
			
		||||
use spacepackets::util::{ToBeBytes, UnsignedEnum};
 | 
			
		||||
 | 
			
		||||
/// Using a type definition allows to change this to u64 in the future more easily
 | 
			
		||||
pub type LargestEventRaw = u32;
 | 
			
		||||
/// Using a type definition allows to change this to u32 in the future more easily
 | 
			
		||||
pub type LargestGroupIdRaw = u16;
 | 
			
		||||
 | 
			
		||||
pub const MAX_GROUP_ID_U32_EVENT: u16 = 2_u16.pow(14) - 1;
 | 
			
		||||
pub const MAX_GROUP_ID_U16_EVENT: u16 = 2_u16.pow(6) - 1;
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum Severity {
 | 
			
		||||
    Info = 0,
 | 
			
		||||
    Low = 1,
 | 
			
		||||
    Medium = 2,
 | 
			
		||||
    High = 3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone {
 | 
			
		||||
    const SEVERITY: Severity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Type level support struct
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
 | 
			
		||||
pub struct SeverityInfo {}
 | 
			
		||||
impl HasSeverity for SeverityInfo {
 | 
			
		||||
    const SEVERITY: Severity = Severity::Info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Type level support struct
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
 | 
			
		||||
pub struct SeverityLow {}
 | 
			
		||||
impl HasSeverity for SeverityLow {
 | 
			
		||||
    const SEVERITY: Severity = Severity::Low;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Type level support struct
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
 | 
			
		||||
pub struct SeverityMedium {}
 | 
			
		||||
impl HasSeverity for SeverityMedium {
 | 
			
		||||
    const SEVERITY: Severity = Severity::Medium;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Type level support struct
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
 | 
			
		||||
pub struct SeverityHigh {}
 | 
			
		||||
impl HasSeverity for SeverityHigh {
 | 
			
		||||
    const SEVERITY: Severity = Severity::High;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait GenericEvent: EcssEnumeration + Copy + Clone {
 | 
			
		||||
    type Raw;
 | 
			
		||||
    type GroupId;
 | 
			
		||||
    type UniqueId;
 | 
			
		||||
 | 
			
		||||
    fn raw(&self) -> Self::Raw;
 | 
			
		||||
    fn severity(&self) -> Severity;
 | 
			
		||||
    fn group_id(&self) -> Self::GroupId;
 | 
			
		||||
    fn unique_id(&self) -> Self::UniqueId;
 | 
			
		||||
 | 
			
		||||
    fn raw_as_largest_type(&self) -> LargestEventRaw;
 | 
			
		||||
    fn group_id_as_largest_type(&self) -> LargestGroupIdRaw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<u8> for Severity {
 | 
			
		||||
    type Error = ();
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: u8) -> Result<Self, Self::Error> {
 | 
			
		||||
        match value {
 | 
			
		||||
            x if x == Severity::Info as u8 => Ok(Severity::Info),
 | 
			
		||||
            x if x == Severity::Low as u8 => Ok(Severity::Low),
 | 
			
		||||
            x if x == Severity::Medium as u8 => Ok(Severity::Medium),
 | 
			
		||||
            x if x == Severity::High as u8 => Ok(Severity::High),
 | 
			
		||||
            _ => Err(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
struct EventBase<Raw, GroupId, UniqueId> {
 | 
			
		||||
    severity: Severity,
 | 
			
		||||
    group_id: GroupId,
 | 
			
		||||
    unique_id: UniqueId,
 | 
			
		||||
    phantom: PhantomData<Raw>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Raw: ToBeBytes, GroupId, UniqueId> EventBase<Raw, GroupId, UniqueId> {
 | 
			
		||||
    fn write_to_bytes(
 | 
			
		||||
        &self,
 | 
			
		||||
        raw: Raw,
 | 
			
		||||
        buf: &mut [u8],
 | 
			
		||||
        width: usize,
 | 
			
		||||
    ) -> Result<usize, ByteConversionError> {
 | 
			
		||||
        if buf.len() < width {
 | 
			
		||||
            return Err(ByteConversionError::ToSliceTooSmall {
 | 
			
		||||
                found: buf.len(),
 | 
			
		||||
                expected: width,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        buf.copy_from_slice(raw.to_be_bytes().as_ref());
 | 
			
		||||
        Ok(raw.written_len())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EventBase<u32, u16, u16> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn raw(&self) -> u32 {
 | 
			
		||||
        ((self.severity as u32) << 30) | ((self.group_id as u32) << 16) | self.unique_id as u32
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EventBase<u16, u8, u8> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn raw(&self) -> u16 {
 | 
			
		||||
        ((self.severity as u16) << 14) | ((self.group_id as u16) << 8) | self.unique_id as u16
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<RAW, GID, UID> EventBase<RAW, GID, UID> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn severity(&self) -> Severity {
 | 
			
		||||
        self.severity
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<RAW, GID> EventBase<RAW, GID, u16> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn unique_id(&self) -> u16 {
 | 
			
		||||
        self.unique_id
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<RAW, GID> EventBase<RAW, GID, u8> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn unique_id(&self) -> u8 {
 | 
			
		||||
        self.unique_id
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<RAW, UID> EventBase<RAW, u16, UID> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn group_id(&self) -> u16 {
 | 
			
		||||
        self.group_id
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<RAW, UID> EventBase<RAW, u8, UID> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn group_id(&self) -> u8 {
 | 
			
		||||
        self.group_id
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! event_provider_impl {
 | 
			
		||||
    () => {
 | 
			
		||||
        #[inline]
 | 
			
		||||
        fn raw(&self) -> Self::Raw {
 | 
			
		||||
            self.base.raw()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// Retrieve the severity of an event. Returns None if that severity bit field of the raw event
 | 
			
		||||
        /// ID is invalid
 | 
			
		||||
        #[inline]
 | 
			
		||||
        fn severity(&self) -> Severity {
 | 
			
		||||
            self.base.severity()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[inline]
 | 
			
		||||
        fn group_id(&self) -> Self::GroupId {
 | 
			
		||||
            self.base.group_id()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[inline]
 | 
			
		||||
        fn unique_id(&self) -> Self::UniqueId {
 | 
			
		||||
            self.base.unique_id()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_event_provider {
 | 
			
		||||
    ($BaseIdent: ident, $TypedIdent: ident, $raw: ty, $gid: ty, $uid: ty) => {
 | 
			
		||||
        impl GenericEvent for $BaseIdent {
 | 
			
		||||
            type Raw = $raw;
 | 
			
		||||
            type GroupId = $gid;
 | 
			
		||||
            type UniqueId = $uid;
 | 
			
		||||
 | 
			
		||||
            event_provider_impl!();
 | 
			
		||||
 | 
			
		||||
            fn raw_as_largest_type(&self) -> LargestEventRaw {
 | 
			
		||||
                self.raw().into()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn group_id_as_largest_type(&self) -> LargestGroupIdRaw {
 | 
			
		||||
                self.group_id().into()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<SEVERITY: HasSeverity> GenericEvent for $TypedIdent<SEVERITY> {
 | 
			
		||||
            type Raw = $raw;
 | 
			
		||||
            type GroupId = $gid;
 | 
			
		||||
            type UniqueId = $uid;
 | 
			
		||||
 | 
			
		||||
            delegate!(to self.event {
 | 
			
		||||
                fn raw(&self) -> Self::Raw;
 | 
			
		||||
                fn severity(&self) -> Severity;
 | 
			
		||||
                fn group_id(&self) -> Self::GroupId;
 | 
			
		||||
                fn unique_id(&self) -> Self::UniqueId;
 | 
			
		||||
                fn raw_as_largest_type(&self) -> LargestEventRaw;
 | 
			
		||||
                fn group_id_as_largest_type(&self) -> LargestGroupIdRaw;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! try_from_impls {
 | 
			
		||||
    ($SevIdent: ident, $severity: path, $raw: ty, $TypedSevIdent: ident) => {
 | 
			
		||||
        impl TryFrom<$raw> for $TypedSevIdent<$SevIdent> {
 | 
			
		||||
            type Error = Severity;
 | 
			
		||||
 | 
			
		||||
            fn try_from(raw: $raw) -> Result<Self, Self::Error> {
 | 
			
		||||
                Self::try_from_generic($severity, raw)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! const_from_fn {
 | 
			
		||||
    ($from_fn_name: ident, $TypedIdent: ident, $SevIdent: ident) => {
 | 
			
		||||
        pub const fn $from_fn_name(event: $TypedIdent<$SevIdent>) -> Self {
 | 
			
		||||
            Self {
 | 
			
		||||
                base: event.event.base,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
pub struct EventU32 {
 | 
			
		||||
    base: EventBase<u32, u16, u16>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
pub struct EventU32TypedSev<SEVERITY> {
 | 
			
		||||
    event: EventU32,
 | 
			
		||||
    phantom: PhantomData<SEVERITY>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SEVERITY: HasSeverity> From<EventU32TypedSev<SEVERITY>> for EventU32 {
 | 
			
		||||
    fn from(e: EventU32TypedSev<SEVERITY>) -> Self {
 | 
			
		||||
        Self { base: e.event.base }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Severity: HasSeverity> AsRef<EventU32> for EventU32TypedSev<Severity> {
 | 
			
		||||
    fn as_ref(&self) -> &EventU32 {
 | 
			
		||||
        &self.event
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Severity: HasSeverity> AsMut<EventU32> for EventU32TypedSev<Severity> {
 | 
			
		||||
    fn as_mut(&mut self) -> &mut EventU32 {
 | 
			
		||||
        &mut self.event
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_event_provider!(EventU32, EventU32TypedSev, u32, u16, u16);
 | 
			
		||||
 | 
			
		||||
impl EventU32 {
 | 
			
		||||
    /// Generate an event. The raw representation of an event has 32 bits.
 | 
			
		||||
    /// If the passed group ID is invalid (too large), None wil be returned
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Parameter
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will
 | 
			
		||||
    ///   be stored inside the uppermost 2 bits of the raw event ID
 | 
			
		||||
    /// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the
 | 
			
		||||
    ///   next 14 bits after the severity. Therefore, the size is limited by dec 16383 hex 0x3FFF.
 | 
			
		||||
    /// * `unique_id`: Each event has a unique 16 bit ID occupying the last 16 bits of the
 | 
			
		||||
    ///   raw event ID
 | 
			
		||||
    pub fn new_checked(
 | 
			
		||||
        severity: Severity,
 | 
			
		||||
        group_id: <Self as GenericEvent>::GroupId,
 | 
			
		||||
        unique_id: <Self as GenericEvent>::UniqueId,
 | 
			
		||||
    ) -> Option<Self> {
 | 
			
		||||
        if group_id > MAX_GROUP_ID_U32_EVENT {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        Some(Self {
 | 
			
		||||
            base: EventBase {
 | 
			
		||||
                severity,
 | 
			
		||||
                group_id,
 | 
			
		||||
                unique_id,
 | 
			
		||||
                phantom: PhantomData,
 | 
			
		||||
            },
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// This constructor will panic if the passed group is is larger than [MAX_GROUP_ID_U32_EVENT].
 | 
			
		||||
    pub const fn new(
 | 
			
		||||
        severity: Severity,
 | 
			
		||||
        group_id: <Self as GenericEvent>::GroupId,
 | 
			
		||||
        unique_id: <Self as GenericEvent>::UniqueId,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        if group_id > MAX_GROUP_ID_U32_EVENT {
 | 
			
		||||
            panic!("Group ID too large");
 | 
			
		||||
        }
 | 
			
		||||
        Self {
 | 
			
		||||
            base: EventBase {
 | 
			
		||||
                severity,
 | 
			
		||||
                group_id,
 | 
			
		||||
                unique_id,
 | 
			
		||||
                phantom: PhantomData,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn from_be_bytes(bytes: [u8; 4]) -> Self {
 | 
			
		||||
        Self::from(u32::from_be_bytes(bytes))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const_from_fn!(const_from_info, EventU32TypedSev, SeverityInfo);
 | 
			
		||||
    const_from_fn!(const_from_low, EventU32TypedSev, SeverityLow);
 | 
			
		||||
    const_from_fn!(const_from_medium, EventU32TypedSev, SeverityMedium);
 | 
			
		||||
    const_from_fn!(const_from_high, EventU32TypedSev, SeverityHigh);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<u32> for EventU32 {
 | 
			
		||||
    fn from(raw: u32) -> Self {
 | 
			
		||||
        // Severity conversion from u8 should never fail
 | 
			
		||||
        let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
 | 
			
		||||
        let group_id = ((raw >> 16) & 0x3FFF) as u16;
 | 
			
		||||
        let unique_id = (raw & 0xFFFF) as u16;
 | 
			
		||||
        // Sanitized input, should never fail
 | 
			
		||||
        Self::new(severity, group_id, unique_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UnsignedEnum for EventU32 {
 | 
			
		||||
    fn size(&self) -> usize {
 | 
			
		||||
        core::mem::size_of::<u32>()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
 | 
			
		||||
        self.base.write_to_bytes(self.raw(), buf, self.size())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn value(&self) -> u64 {
 | 
			
		||||
        self.raw().into()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EcssEnumeration for EventU32 {
 | 
			
		||||
    fn pfc(&self) -> u8 {
 | 
			
		||||
        u32::BITS as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SEVERITY: HasSeverity> EventU32TypedSev<SEVERITY> {
 | 
			
		||||
    /// This is similar to [EventU32::new] but the severity is a type generic, which allows to
 | 
			
		||||
    /// have distinct types for events with different severities
 | 
			
		||||
    pub fn new_checked(
 | 
			
		||||
        group_id: <Self as GenericEvent>::GroupId,
 | 
			
		||||
        unique_id: <Self as GenericEvent>::UniqueId,
 | 
			
		||||
    ) -> Option<Self> {
 | 
			
		||||
        let event = EventU32::new_checked(SEVERITY::SEVERITY, group_id, unique_id)?;
 | 
			
		||||
        Some(Self {
 | 
			
		||||
            event,
 | 
			
		||||
            phantom: PhantomData,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U32_EVENT].
 | 
			
		||||
    pub const fn new(
 | 
			
		||||
        group_id: <Self as GenericEvent>::GroupId,
 | 
			
		||||
        unique_id: <Self as GenericEvent>::UniqueId,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let event = EventU32::new(SEVERITY::SEVERITY, group_id, unique_id);
 | 
			
		||||
        Self {
 | 
			
		||||
            event,
 | 
			
		||||
            phantom: PhantomData,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn try_from_generic(expected: Severity, raw: u32) -> Result<Self, Severity> {
 | 
			
		||||
        let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
 | 
			
		||||
        if severity != expected {
 | 
			
		||||
            return Err(severity);
 | 
			
		||||
        }
 | 
			
		||||
        Ok(Self::new(
 | 
			
		||||
            ((raw >> 16) & 0x3FFF) as u16,
 | 
			
		||||
            (raw & 0xFFFF) as u16,
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
try_from_impls!(SeverityInfo, Severity::Info, u32, EventU32TypedSev);
 | 
			
		||||
try_from_impls!(SeverityLow, Severity::Low, u32, EventU32TypedSev);
 | 
			
		||||
try_from_impls!(SeverityMedium, Severity::Medium, u32, EventU32TypedSev);
 | 
			
		||||
try_from_impls!(SeverityHigh, Severity::High, u32, EventU32TypedSev);
 | 
			
		||||
 | 
			
		||||
//noinspection RsTraitImplementation
 | 
			
		||||
impl<SEVERITY: HasSeverity> UnsignedEnum for EventU32TypedSev<SEVERITY> {
 | 
			
		||||
    delegate!(to self.event {
 | 
			
		||||
        fn size(&self) -> usize;
 | 
			
		||||
        fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
 | 
			
		||||
        fn value(&self) -> u64;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//noinspection RsTraitImplementation
 | 
			
		||||
impl<SEVERITY: HasSeverity> EcssEnumeration for EventU32TypedSev<SEVERITY> {
 | 
			
		||||
    delegate!(to self.event {
 | 
			
		||||
        fn pfc(&self) -> u8;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct EventU16 {
 | 
			
		||||
    base: EventBase<u16, u8, u8>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
pub struct EventU16TypedSev<SEVERITY> {
 | 
			
		||||
    event: EventU16,
 | 
			
		||||
    phantom: PhantomData<SEVERITY>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Severity: HasSeverity> AsRef<EventU16> for EventU16TypedSev<Severity> {
 | 
			
		||||
    fn as_ref(&self) -> &EventU16 {
 | 
			
		||||
        &self.event
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Severity: HasSeverity> AsMut<EventU16> for EventU16TypedSev<Severity> {
 | 
			
		||||
    fn as_mut(&mut self) -> &mut EventU16 {
 | 
			
		||||
        &mut self.event
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EventU16 {
 | 
			
		||||
    /// Generate a small event. The raw representation of a small event has 16 bits.
 | 
			
		||||
    /// If the passed group ID is invalid (too large), [None] wil be returned
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Parameter
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will
 | 
			
		||||
    ///   be stored inside the uppermost 2 bits of the raw event ID
 | 
			
		||||
    /// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the
 | 
			
		||||
    ///   next 6 bits after the severity. Therefore, the size is limited by dec 63 hex 0x3F.
 | 
			
		||||
    /// * `unique_id`: Each event has a unique 8 bit ID occupying the last 8 bits of the
 | 
			
		||||
    ///   raw event ID
 | 
			
		||||
    pub fn new_checked(
 | 
			
		||||
        severity: Severity,
 | 
			
		||||
        group_id: <Self as GenericEvent>::GroupId,
 | 
			
		||||
        unique_id: <Self as GenericEvent>::UniqueId,
 | 
			
		||||
    ) -> Option<Self> {
 | 
			
		||||
        if group_id > (2u8.pow(6) - 1) {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        Some(Self {
 | 
			
		||||
            base: EventBase {
 | 
			
		||||
                severity,
 | 
			
		||||
                group_id,
 | 
			
		||||
                unique_id,
 | 
			
		||||
                phantom: Default::default(),
 | 
			
		||||
            },
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT].
 | 
			
		||||
    pub const fn new(
 | 
			
		||||
        severity: Severity,
 | 
			
		||||
        group_id: <Self as GenericEvent>::GroupId,
 | 
			
		||||
        unique_id: <Self as GenericEvent>::UniqueId,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        if group_id > (2u8.pow(6) - 1) {
 | 
			
		||||
            panic!("Group ID too large");
 | 
			
		||||
        }
 | 
			
		||||
        Self {
 | 
			
		||||
            base: EventBase {
 | 
			
		||||
                severity,
 | 
			
		||||
                group_id,
 | 
			
		||||
                unique_id,
 | 
			
		||||
                phantom: PhantomData,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
 | 
			
		||||
        Self::from(u16::from_be_bytes(bytes))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const_from_fn!(const_from_info, EventU16TypedSev, SeverityInfo);
 | 
			
		||||
    const_from_fn!(const_from_low, EventU16TypedSev, SeverityLow);
 | 
			
		||||
    const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium);
 | 
			
		||||
    const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<u16> for EventU16 {
 | 
			
		||||
    fn from(raw: <Self as GenericEvent>::Raw) -> Self {
 | 
			
		||||
        let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
 | 
			
		||||
        let group_id = ((raw >> 8) & 0x3F) as u8;
 | 
			
		||||
        let unique_id = (raw & 0xFF) as u8;
 | 
			
		||||
        // Sanitized input, new call should never fail
 | 
			
		||||
        Self::new(severity, group_id, unique_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UnsignedEnum for EventU16 {
 | 
			
		||||
    fn size(&self) -> usize {
 | 
			
		||||
        core::mem::size_of::<u16>()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
 | 
			
		||||
        self.base.write_to_bytes(self.raw(), buf, self.size())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn value(&self) -> u64 {
 | 
			
		||||
        self.raw().into()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl EcssEnumeration for EventU16 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn pfc(&self) -> u8 {
 | 
			
		||||
        u16::BITS as u8
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<SEVERITY: HasSeverity> EventU16TypedSev<SEVERITY> {
 | 
			
		||||
    /// This is similar to [EventU16::new] but the severity is a type generic, which allows to
 | 
			
		||||
    /// have distinct types for events with different severities
 | 
			
		||||
    pub fn new_checked(
 | 
			
		||||
        group_id: <Self as GenericEvent>::GroupId,
 | 
			
		||||
        unique_id: <Self as GenericEvent>::UniqueId,
 | 
			
		||||
    ) -> Option<Self> {
 | 
			
		||||
        let event = EventU16::new_checked(SEVERITY::SEVERITY, group_id, unique_id)?;
 | 
			
		||||
        Some(Self {
 | 
			
		||||
            event,
 | 
			
		||||
            phantom: PhantomData,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT].
 | 
			
		||||
    pub const fn new(
 | 
			
		||||
        group_id: <Self as GenericEvent>::GroupId,
 | 
			
		||||
        unique_id: <Self as GenericEvent>::UniqueId,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id);
 | 
			
		||||
        Self {
 | 
			
		||||
            event,
 | 
			
		||||
            phantom: PhantomData,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn try_from_generic(expected: Severity, raw: u16) -> Result<Self, Severity> {
 | 
			
		||||
        let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
 | 
			
		||||
        if severity != expected {
 | 
			
		||||
            return Err(severity);
 | 
			
		||||
        }
 | 
			
		||||
        Ok(Self::new(((raw >> 8) & 0x3F) as u8, (raw & 0xFF) as u8))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8);
 | 
			
		||||
 | 
			
		||||
//noinspection RsTraitImplementation
 | 
			
		||||
impl<SEVERITY: HasSeverity> UnsignedEnum for EventU16TypedSev<SEVERITY> {
 | 
			
		||||
    delegate!(to self.event {
 | 
			
		||||
        fn size(&self) -> usize;
 | 
			
		||||
        fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
 | 
			
		||||
        fn value(&self) -> u64;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//noinspection RsTraitImplementation
 | 
			
		||||
impl<SEVERITY: HasSeverity> EcssEnumeration for EventU16TypedSev<SEVERITY> {
 | 
			
		||||
    delegate!(to self.event {
 | 
			
		||||
        fn pfc(&self) -> u8;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
try_from_impls!(SeverityInfo, Severity::Info, u16, EventU16TypedSev);
 | 
			
		||||
try_from_impls!(SeverityLow, Severity::Low, u16, EventU16TypedSev);
 | 
			
		||||
try_from_impls!(SeverityMedium, Severity::Medium, u16, EventU16TypedSev);
 | 
			
		||||
try_from_impls!(SeverityHigh, Severity::High, u16, EventU16TypedSev);
 | 
			
		||||
 | 
			
		||||
impl<Severity: HasSeverity> PartialEq<EventU32> for EventU32TypedSev<Severity> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn eq(&self, other: &EventU32) -> bool {
 | 
			
		||||
        self.raw() == other.raw()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Severity: HasSeverity> PartialEq<EventU32TypedSev<Severity>> for EventU32 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn eq(&self, other: &EventU32TypedSev<Severity>) -> bool {
 | 
			
		||||
        self.raw() == other.raw()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Severity: HasSeverity> PartialEq<EventU16> for EventU16TypedSev<Severity> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn eq(&self, other: &EventU16) -> bool {
 | 
			
		||||
        self.raw() == other.raw()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Severity: HasSeverity> PartialEq<EventU16TypedSev<Severity>> for EventU16 {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn eq(&self, other: &EventU16TypedSev<Severity>) -> bool {
 | 
			
		||||
        self.raw() == other.raw()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::EventU32TypedSev;
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use spacepackets::ByteConversionError;
 | 
			
		||||
    use std::mem::size_of;
 | 
			
		||||
 | 
			
		||||
    fn assert_size<T>(_: T, val: usize) {
 | 
			
		||||
        assert_eq!(size_of::<T>(), val);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(0, 0);
 | 
			
		||||
    const INFO_EVENT_SMALL: EventU16TypedSev<SeverityInfo> = EventU16TypedSev::new(0, 0);
 | 
			
		||||
    const HIGH_SEV_EVENT: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::new(0x3FFF, 0xFFFF);
 | 
			
		||||
    const HIGH_SEV_EVENT_SMALL: EventU16TypedSev<SeverityHigh> = EventU16TypedSev::new(0x3F, 0xff);
 | 
			
		||||
 | 
			
		||||
    /// This working is a test in itself.
 | 
			
		||||
    const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT);
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_normal_from_raw_conversion() {
 | 
			
		||||
        let conv_from_raw = EventU32TypedSev::<SeverityInfo>::try_from(INFO_EVENT.raw())
 | 
			
		||||
            .expect("Creating typed EventU32 failed");
 | 
			
		||||
        assert_eq!(conv_from_raw, INFO_EVENT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_small_from_raw_conversion() {
 | 
			
		||||
        let conv_from_raw = EventU16TypedSev::<SeverityInfo>::try_from(INFO_EVENT_SMALL.raw())
 | 
			
		||||
            .expect("Creating typed EventU16 failed");
 | 
			
		||||
        assert_eq!(conv_from_raw, INFO_EVENT_SMALL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn verify_normal_size() {
 | 
			
		||||
        assert_size(INFO_EVENT.raw(), 4)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn verify_small_size() {
 | 
			
		||||
        assert_size(INFO_EVENT_SMALL.raw(), 2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_normal_event_getters() {
 | 
			
		||||
        assert_eq!(INFO_EVENT.severity(), Severity::Info);
 | 
			
		||||
        assert_eq!(INFO_EVENT.unique_id(), 0);
 | 
			
		||||
        assert_eq!(INFO_EVENT.group_id(), 0);
 | 
			
		||||
        let raw_event = INFO_EVENT.raw();
 | 
			
		||||
        assert_eq!(raw_event, 0x00000000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_small_event_getters() {
 | 
			
		||||
        assert_eq!(INFO_EVENT_SMALL.severity(), Severity::Info);
 | 
			
		||||
        assert_eq!(INFO_EVENT_SMALL.unique_id(), 0);
 | 
			
		||||
        assert_eq!(INFO_EVENT_SMALL.group_id(), 0);
 | 
			
		||||
        let raw_event = INFO_EVENT_SMALL.raw();
 | 
			
		||||
        assert_eq!(raw_event, 0x00000000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn all_ones_event_regular() {
 | 
			
		||||
        assert_eq!(HIGH_SEV_EVENT.severity(), Severity::High);
 | 
			
		||||
        assert_eq!(HIGH_SEV_EVENT.group_id(), 0x3FFF);
 | 
			
		||||
        assert_eq!(HIGH_SEV_EVENT.unique_id(), 0xFFFF);
 | 
			
		||||
        let raw_event = HIGH_SEV_EVENT.raw();
 | 
			
		||||
        assert_eq!(raw_event, 0xFFFFFFFF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn all_ones_event_small() {
 | 
			
		||||
        assert_eq!(HIGH_SEV_EVENT_SMALL.severity(), Severity::High);
 | 
			
		||||
        assert_eq!(HIGH_SEV_EVENT_SMALL.group_id(), 0x3F);
 | 
			
		||||
        assert_eq!(HIGH_SEV_EVENT_SMALL.unique_id(), 0xFF);
 | 
			
		||||
        let raw_event = HIGH_SEV_EVENT_SMALL.raw();
 | 
			
		||||
        assert_eq!(raw_event, 0xFFFF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn invalid_group_id_normal() {
 | 
			
		||||
        assert!(EventU32TypedSev::<SeverityMedium>::new_checked(2_u16.pow(14), 0).is_none());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn invalid_group_id_small() {
 | 
			
		||||
        assert!(EventU16TypedSev::<SeverityMedium>::new_checked(2_u8.pow(6), 0).is_none());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn regular_new() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            EventU32TypedSev::<SeverityInfo>::new_checked(0, 0)
 | 
			
		||||
                .expect("Creating regular event failed"),
 | 
			
		||||
            INFO_EVENT
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn small_new() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            EventU16TypedSev::<SeverityInfo>::new_checked(0, 0)
 | 
			
		||||
                .expect("Creating regular event failed"),
 | 
			
		||||
            INFO_EVENT_SMALL
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn as_largest_type() {
 | 
			
		||||
        let event_raw = HIGH_SEV_EVENT.raw_as_largest_type();
 | 
			
		||||
        assert_size(event_raw, 4);
 | 
			
		||||
        assert_eq!(event_raw, 0xFFFFFFFF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn as_largest_type_for_small_event() {
 | 
			
		||||
        let event_raw = HIGH_SEV_EVENT_SMALL.raw_as_largest_type();
 | 
			
		||||
        assert_size(event_raw, 4);
 | 
			
		||||
        assert_eq!(event_raw, 0xFFFF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn as_largest_group_id() {
 | 
			
		||||
        let group_id = HIGH_SEV_EVENT.group_id_as_largest_type();
 | 
			
		||||
        assert_size(group_id, 2);
 | 
			
		||||
        assert_eq!(group_id, 0x3FFF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn as_largest_group_id_small_event() {
 | 
			
		||||
        let group_id = HIGH_SEV_EVENT_SMALL.group_id_as_largest_type();
 | 
			
		||||
        assert_size(group_id, 2);
 | 
			
		||||
        assert_eq!(group_id, 0x3F);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn write_to_buf() {
 | 
			
		||||
        let mut buf: [u8; 4] = [0; 4];
 | 
			
		||||
        assert!(HIGH_SEV_EVENT.write_to_be_bytes(&mut buf).is_ok());
 | 
			
		||||
        let val_from_raw = u32::from_be_bytes(buf);
 | 
			
		||||
        assert_eq!(val_from_raw, 0xFFFFFFFF);
 | 
			
		||||
        let event_read_back = EventU32::from_be_bytes(buf);
 | 
			
		||||
        assert_eq!(event_read_back, HIGH_SEV_EVENT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn write_to_buf_small() {
 | 
			
		||||
        let mut buf: [u8; 2] = [0; 2];
 | 
			
		||||
        assert!(HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf).is_ok());
 | 
			
		||||
        let val_from_raw = u16::from_be_bytes(buf);
 | 
			
		||||
        assert_eq!(val_from_raw, 0xFFFF);
 | 
			
		||||
        let event_read_back = EventU16::from_be_bytes(buf);
 | 
			
		||||
        assert_eq!(event_read_back, HIGH_SEV_EVENT_SMALL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn write_to_buf_insufficient_buf() {
 | 
			
		||||
        let mut buf: [u8; 3] = [0; 3];
 | 
			
		||||
        let err = HIGH_SEV_EVENT.write_to_be_bytes(&mut buf);
 | 
			
		||||
        assert!(err.is_err());
 | 
			
		||||
        let err = err.unwrap_err();
 | 
			
		||||
        if let ByteConversionError::ToSliceTooSmall { found, expected } = err {
 | 
			
		||||
            assert_eq!(expected, 4);
 | 
			
		||||
            assert_eq!(found, 3);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn write_to_buf_small_insufficient_buf() {
 | 
			
		||||
        let mut buf: [u8; 1] = [0; 1];
 | 
			
		||||
        let err = HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf);
 | 
			
		||||
        assert!(err.is_err());
 | 
			
		||||
        let err = err.unwrap_err();
 | 
			
		||||
        if let ByteConversionError::ToSliceTooSmall { found, expected } = err {
 | 
			
		||||
            assert_eq!(expected, 2);
 | 
			
		||||
            assert_eq!(found, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn severity_from_invalid_raw_val() {
 | 
			
		||||
        let invalid = 0xFF;
 | 
			
		||||
        assert!(Severity::try_from(invalid).is_err());
 | 
			
		||||
        let invalid = Severity::High as u8 + 1;
 | 
			
		||||
        assert!(Severity::try_from(invalid).is_err());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn reduction() {
 | 
			
		||||
        let event = EventU32TypedSev::<SeverityInfo>::new(1, 1);
 | 
			
		||||
        let raw = event.raw();
 | 
			
		||||
        let reduced: EventU32 = event.into();
 | 
			
		||||
        assert_eq!(reduced.group_id(), 1);
 | 
			
		||||
        assert_eq!(reduced.unique_id(), 1);
 | 
			
		||||
        assert_eq!(raw, reduced.raw());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn const_reducation() {
 | 
			
		||||
        assert_eq!(INFO_REDUCED.raw(), INFO_EVENT.raw());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -26,8 +26,7 @@ use std::vec::Vec;
 | 
			
		||||
/// use satrs::ComponentId;
 | 
			
		||||
/// use satrs::tmtc::PacketSenderRaw;
 | 
			
		||||
/// use spacepackets::SpHeader;
 | 
			
		||||
/// use spacepackets::ecss::tc::{PusTcCreator, CreatorConfig};
 | 
			
		||||
/// use arbitrary_int::u11;
 | 
			
		||||
/// use spacepackets::ecss::tc::PusTcCreator;
 | 
			
		||||
///
 | 
			
		||||
/// const UDP_SERVER_ID: ComponentId = 0x05;
 | 
			
		||||
///
 | 
			
		||||
@@ -35,8 +34,8 @@ use std::vec::Vec;
 | 
			
		||||
/// let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777);
 | 
			
		||||
/// let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, packet_sender)
 | 
			
		||||
///       .expect("Creating UDP TMTC server failed");
 | 
			
		||||
/// let sph = SpHeader::new_from_apid(u11::new(0x02));
 | 
			
		||||
/// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
 | 
			
		||||
/// let sph = SpHeader::new_from_apid(0x02);
 | 
			
		||||
/// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
 | 
			
		||||
/// // Can not fail.
 | 
			
		||||
/// let ping_tc_raw = pus_tc.to_vec().unwrap();
 | 
			
		||||
///
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
//!    the [ECSS PUS C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 | 
			
		||||
#[cfg(any(feature = "alloc", test))]
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
extern crate alloc;
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
extern crate downcast_rs;
 | 
			
		||||
@@ -27,9 +27,7 @@ pub mod action;
 | 
			
		||||
pub mod dev_mgmt;
 | 
			
		||||
pub mod encoding;
 | 
			
		||||
pub mod event_man;
 | 
			
		||||
pub mod event_man_legacy;
 | 
			
		||||
pub mod events;
 | 
			
		||||
pub mod events_legacy;
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
pub mod executable;
 | 
			
		||||
pub mod hal;
 | 
			
		||||
@@ -51,7 +49,6 @@ pub mod scheduling;
 | 
			
		||||
pub mod subsystem;
 | 
			
		||||
pub mod time;
 | 
			
		||||
pub mod tmtc;
 | 
			
		||||
pub mod ccsds;
 | 
			
		||||
 | 
			
		||||
pub use spacepackets;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -258,9 +258,6 @@ pub trait PoolProvider {
 | 
			
		||||
 | 
			
		||||
    /// Delete data inside the pool given a [PoolAddr].
 | 
			
		||||
    fn delete(&mut self, addr: PoolAddr) -> Result<(), PoolError>;
 | 
			
		||||
 | 
			
		||||
    fn clear(&mut self) -> Result<(), PoolError>;
 | 
			
		||||
 | 
			
		||||
    fn has_element_at(&self, addr: &PoolAddr) -> Result<bool, PoolError>;
 | 
			
		||||
 | 
			
		||||
    /// Retrieve the length of the data at the given store address.
 | 
			
		||||
@@ -717,13 +714,6 @@ pub mod heapless_mod {
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn clear(&mut self) -> Result<(), PoolError> {
 | 
			
		||||
            for size in self.sizes_lists.iter_mut() {
 | 
			
		||||
                size.fill(STORE_FREE);
 | 
			
		||||
            }
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn has_element_at(&self, addr: &PoolAddr) -> Result<bool, PoolError> {
 | 
			
		||||
            let addr = StaticPoolAddr::from(*addr);
 | 
			
		||||
            self.validate_addr(&addr)?;
 | 
			
		||||
@@ -1065,13 +1055,6 @@ mod alloc_mod {
 | 
			
		||||
                _ => size,
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn clear(&mut self) -> Result<(), PoolError> {
 | 
			
		||||
            for size in self.sizes_lists.iter_mut() {
 | 
			
		||||
                size.fill(STORE_FREE);
 | 
			
		||||
            }
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl PoolProviderWithGuards for StaticMemoryPool {
 | 
			
		||||
@@ -1614,228 +1597,88 @@ mod tests {
 | 
			
		||||
    mod heapless_tests {
 | 
			
		||||
        use super::*;
 | 
			
		||||
        use crate::static_subpool;
 | 
			
		||||
        use std::cell::UnsafeCell;
 | 
			
		||||
        use std::sync::Mutex;
 | 
			
		||||
 | 
			
		||||
        const SUBPOOL_1_BLOCK_SIZE: usize = 4;
 | 
			
		||||
        const SUBPOOL_1_NUM_ELEMENTS: u16 = 4;
 | 
			
		||||
 | 
			
		||||
        static SUBPOOL_1: static_cell::ConstStaticCell<
 | 
			
		||||
            [u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
 | 
			
		||||
        > = static_cell::ConstStaticCell::new(
 | 
			
		||||
            [0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        static SUBPOOL_1_SIZES: Mutex<UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>> =
 | 
			
		||||
            Mutex::new(UnsafeCell::new(
 | 
			
		||||
                [STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize],
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
        const SUBPOOL_2_NUM_ELEMENTS: u16 = 2;
 | 
			
		||||
        const SUBPOOL_2_BLOCK_SIZE: usize = 8;
 | 
			
		||||
        static SUBPOOL_2: static_cell::ConstStaticCell<
 | 
			
		||||
            [u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
 | 
			
		||||
        > = static_cell::ConstStaticCell::new(
 | 
			
		||||
            [0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
 | 
			
		||||
        );
 | 
			
		||||
        static SUBPOOL_2_SIZES: static_cell::ConstStaticCell<
 | 
			
		||||
            [usize; SUBPOOL_2_NUM_ELEMENTS as usize],
 | 
			
		||||
        > = static_cell::ConstStaticCell::new([STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]);
 | 
			
		||||
 | 
			
		||||
        const SUBPOOL_3_NUM_ELEMENTS: u16 = 1;
 | 
			
		||||
        const SUBPOOL_3_BLOCK_SIZE: usize = 16;
 | 
			
		||||
        static_subpool!(
 | 
			
		||||
            SUBPOOL_3,
 | 
			
		||||
            SUBPOOL_3_SIZES,
 | 
			
		||||
            SUBPOOL_3_NUM_ELEMENTS as usize,
 | 
			
		||||
            SUBPOOL_3_BLOCK_SIZE
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const SUBPOOL_4_NUM_ELEMENTS: u16 = 2;
 | 
			
		||||
        const SUBPOOL_4_BLOCK_SIZE: usize = 16;
 | 
			
		||||
        static_subpool!(
 | 
			
		||||
            SUBPOOL_4,
 | 
			
		||||
            SUBPOOL_4_SIZES,
 | 
			
		||||
            SUBPOOL_4_NUM_ELEMENTS as usize,
 | 
			
		||||
            SUBPOOL_4_BLOCK_SIZE
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const SUBPOOL_5_NUM_ELEMENTS: u16 = 1;
 | 
			
		||||
        const SUBPOOL_5_BLOCK_SIZE: usize = 8;
 | 
			
		||||
        static_subpool!(
 | 
			
		||||
            SUBPOOL_5,
 | 
			
		||||
            SUBPOOL_5_SIZES,
 | 
			
		||||
            SUBPOOL_5_NUM_ELEMENTS as usize,
 | 
			
		||||
            SUBPOOL_5_BLOCK_SIZE
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const SUBPOOL_6_NUM_ELEMENTS: u16 = 1;
 | 
			
		||||
        const SUBPOOL_6_BLOCK_SIZE: usize = 12;
 | 
			
		||||
        static_subpool!(
 | 
			
		||||
            SUBPOOL_6,
 | 
			
		||||
            SUBPOOL_6_SIZES,
 | 
			
		||||
            SUBPOOL_6_NUM_ELEMENTS as usize,
 | 
			
		||||
            SUBPOOL_6_BLOCK_SIZE
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        macro_rules! make_heapless_pool {
 | 
			
		||||
            ($prefix:ident) => {{
 | 
			
		||||
                paste::paste! {
 | 
			
		||||
                    static [<$prefix _SUBPOOL_1>]: static_cell::ConstStaticCell<
 | 
			
		||||
                        [u8; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
 | 
			
		||||
                    > = static_cell::ConstStaticCell::new(
 | 
			
		||||
                        [0; SUBPOOL_1_NUM_ELEMENTS as usize * SUBPOOL_1_BLOCK_SIZE],
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    static [<$prefix _SUBPOOL_1_SIZES>]: std::sync::Mutex<
 | 
			
		||||
                        std::cell::UnsafeCell<[usize; SUBPOOL_1_NUM_ELEMENTS as usize]>
 | 
			
		||||
                    > = std::sync::Mutex::new(
 | 
			
		||||
                        std::cell::UnsafeCell::new([STORE_FREE; SUBPOOL_1_NUM_ELEMENTS as usize])
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    static [<$prefix _SUBPOOL_2>]: static_cell::ConstStaticCell<
 | 
			
		||||
                        [u8; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
 | 
			
		||||
                    > = static_cell::ConstStaticCell::new(
 | 
			
		||||
                        [0; SUBPOOL_2_NUM_ELEMENTS as usize * SUBPOOL_2_BLOCK_SIZE],
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    static [<$prefix _SUBPOOL_2_SIZES>]: static_cell::ConstStaticCell<
 | 
			
		||||
                        [usize; SUBPOOL_2_NUM_ELEMENTS as usize],
 | 
			
		||||
                    > = static_cell::ConstStaticCell::new(
 | 
			
		||||
                        [STORE_FREE; SUBPOOL_2_NUM_ELEMENTS as usize]
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    static [<$prefix _SUBPOOL_3>]: static_cell::ConstStaticCell<
 | 
			
		||||
                        [u8; SUBPOOL_3_NUM_ELEMENTS as usize * SUBPOOL_3_BLOCK_SIZE],
 | 
			
		||||
                    > = static_cell::ConstStaticCell::new(
 | 
			
		||||
                        [0; SUBPOOL_3_NUM_ELEMENTS as usize * SUBPOOL_3_BLOCK_SIZE],
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    static [<$prefix _SUBPOOL_3_SIZES>]: static_cell::ConstStaticCell<
 | 
			
		||||
                        [usize; SUBPOOL_3_NUM_ELEMENTS as usize],
 | 
			
		||||
                    > = static_cell::ConstStaticCell::new(
 | 
			
		||||
                        [STORE_FREE; SUBPOOL_3_NUM_ELEMENTS as usize]
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    let mut heapless_pool: StaticHeaplessMemoryPool<3> =
 | 
			
		||||
                        StaticHeaplessMemoryPool::new(false);
 | 
			
		||||
 | 
			
		||||
                    heapless_pool
 | 
			
		||||
                        .grow(
 | 
			
		||||
                            [<$prefix _SUBPOOL_1>].take(),
 | 
			
		||||
                            unsafe { &mut *[<$prefix _SUBPOOL_1_SIZES>].lock().unwrap().get() },
 | 
			
		||||
                            SUBPOOL_1_NUM_ELEMENTS,
 | 
			
		||||
                            true
 | 
			
		||||
                        )
 | 
			
		||||
                        .unwrap();
 | 
			
		||||
 | 
			
		||||
                    heapless_pool
 | 
			
		||||
                        .grow(
 | 
			
		||||
                            [<$prefix _SUBPOOL_2>].take(),
 | 
			
		||||
                            [<$prefix _SUBPOOL_2_SIZES>].take(),
 | 
			
		||||
                            SUBPOOL_2_NUM_ELEMENTS,
 | 
			
		||||
                            true
 | 
			
		||||
                        )
 | 
			
		||||
                        .unwrap();
 | 
			
		||||
 | 
			
		||||
                    heapless_pool
 | 
			
		||||
                        .grow(
 | 
			
		||||
                            [<$prefix _SUBPOOL_3>].take(),
 | 
			
		||||
                            [<$prefix _SUBPOOL_3_SIZES>].take(),
 | 
			
		||||
                            SUBPOOL_3_NUM_ELEMENTS,
 | 
			
		||||
                            true
 | 
			
		||||
                        )
 | 
			
		||||
                        .unwrap();
 | 
			
		||||
 | 
			
		||||
                    heapless_pool
 | 
			
		||||
                }
 | 
			
		||||
            }};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_heapless_add_and_read() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T0);
 | 
			
		||||
            generic_test_add_and_read::<16>(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_add_smaller_than_full_slot() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T1);
 | 
			
		||||
            generic_test_add_smaller_than_full_slot(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_delete() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T2);
 | 
			
		||||
            generic_test_delete(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_modify() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T3);
 | 
			
		||||
            generic_test_modify(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_consecutive_reservation() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T4);
 | 
			
		||||
            generic_test_consecutive_reservation(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_read_does_not_exist() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T5);
 | 
			
		||||
            generic_test_read_does_not_exist(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_store_full() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T6);
 | 
			
		||||
            generic_test_store_full(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_invalid_pool_idx() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T7);
 | 
			
		||||
            generic_test_invalid_pool_idx(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_invalid_packet_idx() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T8);
 | 
			
		||||
            generic_test_invalid_packet_idx(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_add_too_large() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T9);
 | 
			
		||||
            generic_test_add_too_large(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_data_too_large_1() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T10);
 | 
			
		||||
            generic_test_data_too_large_1(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_free_element_too_large() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T11);
 | 
			
		||||
            generic_test_free_element_too_large(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_guard_deletion_man_creation() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T12);
 | 
			
		||||
            generic_test_pool_guard_deletion_man_creation(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_guard_deletion() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T13);
 | 
			
		||||
            generic_test_pool_guard_deletion(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_guard_with_release() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T14);
 | 
			
		||||
            generic_test_pool_guard_with_release(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_modify_guard_man_creation() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T15);
 | 
			
		||||
            generic_test_pool_modify_guard_man_creation(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_modify_guard() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T16);
 | 
			
		||||
            generic_test_pool_modify_guard(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn modify_pool_index_above_0() {
 | 
			
		||||
            let mut pool_provider = make_heapless_pool!(T17);
 | 
			
		||||
            generic_modify_pool_index_above_0(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_spills_to_higher_subpools() {
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_2_T18,
 | 
			
		||||
                SUBPOOL_2_SIZES_T18,
 | 
			
		||||
                SUBPOOL_2_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_2_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_4_T18,
 | 
			
		||||
                SUBPOOL_4_SIZES_T18,
 | 
			
		||||
                SUBPOOL_4_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_4_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            let mut heapless_pool: StaticHeaplessMemoryPool<2> =
 | 
			
		||||
                StaticHeaplessMemoryPool::new(true);
 | 
			
		||||
        fn small_heapless_pool() -> StaticHeaplessMemoryPool<3> {
 | 
			
		||||
            let mut heapless_pool: StaticHeaplessMemoryPool<3> =
 | 
			
		||||
                StaticHeaplessMemoryPool::new(false);
 | 
			
		||||
            assert!(
 | 
			
		||||
                heapless_pool
 | 
			
		||||
                    .grow(
 | 
			
		||||
                        SUBPOOL_2_T18.take(),
 | 
			
		||||
                        SUBPOOL_2_SIZES_T18.take(),
 | 
			
		||||
                        SUBPOOL_1.take(),
 | 
			
		||||
                        unsafe { &mut *SUBPOOL_1_SIZES.lock().unwrap().get() },
 | 
			
		||||
                        SUBPOOL_1_NUM_ELEMENTS,
 | 
			
		||||
                        true
 | 
			
		||||
                    )
 | 
			
		||||
                    .is_ok()
 | 
			
		||||
            );
 | 
			
		||||
            assert!(
 | 
			
		||||
                heapless_pool
 | 
			
		||||
                    .grow(
 | 
			
		||||
                        SUBPOOL_2.take(),
 | 
			
		||||
                        SUBPOOL_2_SIZES.take(),
 | 
			
		||||
                        SUBPOOL_2_NUM_ELEMENTS,
 | 
			
		||||
                        true
 | 
			
		||||
                    )
 | 
			
		||||
@@ -1844,8 +1687,143 @@ mod tests {
 | 
			
		||||
            assert!(
 | 
			
		||||
                heapless_pool
 | 
			
		||||
                    .grow(
 | 
			
		||||
                        SUBPOOL_4_T18.take(),
 | 
			
		||||
                        SUBPOOL_4_SIZES_T18.take(),
 | 
			
		||||
                        SUBPOOL_3.take(),
 | 
			
		||||
                        SUBPOOL_3_SIZES.take(),
 | 
			
		||||
                        SUBPOOL_3_NUM_ELEMENTS,
 | 
			
		||||
                        true
 | 
			
		||||
                    )
 | 
			
		||||
                    .is_ok()
 | 
			
		||||
            );
 | 
			
		||||
            heapless_pool
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_heapless_add_and_read() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_add_and_read::<16>(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_add_smaller_than_full_slot() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_add_smaller_than_full_slot(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_delete() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_delete(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_modify() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_modify(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_consecutive_reservation() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_consecutive_reservation(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_read_does_not_exist() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_read_does_not_exist(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_store_full() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_store_full(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_invalid_pool_idx() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_invalid_pool_idx(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_invalid_packet_idx() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_invalid_packet_idx(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_add_too_large() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_add_too_large(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_data_too_large_1() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_data_too_large_1(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_free_element_too_large() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_free_element_too_large(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_guard_deletion_man_creation() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_pool_guard_deletion_man_creation(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_guard_deletion() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_pool_guard_deletion(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_guard_with_release() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_pool_guard_with_release(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_modify_guard_man_creation() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_pool_modify_guard_man_creation(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_pool_modify_guard() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_test_pool_modify_guard(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn modify_pool_index_above_0() {
 | 
			
		||||
            let mut pool_provider = small_heapless_pool();
 | 
			
		||||
            generic_modify_pool_index_above_0(&mut pool_provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_spills_to_higher_subpools() {
 | 
			
		||||
            let mut heapless_pool: StaticHeaplessMemoryPool<2> =
 | 
			
		||||
                StaticHeaplessMemoryPool::new(true);
 | 
			
		||||
            assert!(
 | 
			
		||||
                heapless_pool
 | 
			
		||||
                    .grow(
 | 
			
		||||
                        SUBPOOL_2.take(),
 | 
			
		||||
                        SUBPOOL_2_SIZES.take(),
 | 
			
		||||
                        SUBPOOL_2_NUM_ELEMENTS,
 | 
			
		||||
                        true
 | 
			
		||||
                    )
 | 
			
		||||
                    .is_ok()
 | 
			
		||||
            );
 | 
			
		||||
            assert!(
 | 
			
		||||
                heapless_pool
 | 
			
		||||
                    .grow(
 | 
			
		||||
                        SUBPOOL_4.take(),
 | 
			
		||||
                        SUBPOOL_4_SIZES.take(),
 | 
			
		||||
                        SUBPOOL_4_NUM_ELEMENTS,
 | 
			
		||||
                        true
 | 
			
		||||
                    )
 | 
			
		||||
@@ -1858,18 +1836,6 @@ mod tests {
 | 
			
		||||
        fn test_spillage_fails_as_well() {
 | 
			
		||||
            let mut heapless_pool: StaticHeaplessMemoryPool<2> =
 | 
			
		||||
                StaticHeaplessMemoryPool::new(true);
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_5,
 | 
			
		||||
                SUBPOOL_5_SIZES,
 | 
			
		||||
                SUBPOOL_5_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_5_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_3,
 | 
			
		||||
                SUBPOOL_3_SIZES,
 | 
			
		||||
                SUBPOOL_3_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_3_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            assert!(
 | 
			
		||||
                heapless_pool
 | 
			
		||||
                    .grow(
 | 
			
		||||
@@ -1897,24 +1863,6 @@ mod tests {
 | 
			
		||||
        fn test_spillage_works_across_multiple_subpools() {
 | 
			
		||||
            let mut heapless_pool: StaticHeaplessMemoryPool<3> =
 | 
			
		||||
                StaticHeaplessMemoryPool::new(true);
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_3,
 | 
			
		||||
                SUBPOOL_3_SIZES,
 | 
			
		||||
                SUBPOOL_3_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_3_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_5,
 | 
			
		||||
                SUBPOOL_5_SIZES,
 | 
			
		||||
                SUBPOOL_5_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_5_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_6,
 | 
			
		||||
                SUBPOOL_6_SIZES,
 | 
			
		||||
                SUBPOOL_6_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_6_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            assert!(
 | 
			
		||||
                heapless_pool
 | 
			
		||||
                    .grow(
 | 
			
		||||
@@ -1950,24 +1898,6 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn test_spillage_fails_across_multiple_subpools() {
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_3,
 | 
			
		||||
                SUBPOOL_3_SIZES,
 | 
			
		||||
                SUBPOOL_3_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_3_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_5,
 | 
			
		||||
                SUBPOOL_5_SIZES,
 | 
			
		||||
                SUBPOOL_5_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_5_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            static_subpool!(
 | 
			
		||||
                SUBPOOL_6,
 | 
			
		||||
                SUBPOOL_6_SIZES,
 | 
			
		||||
                SUBPOOL_6_NUM_ELEMENTS as usize,
 | 
			
		||||
                SUBPOOL_6_BLOCK_SIZE
 | 
			
		||||
            );
 | 
			
		||||
            let mut heapless_pool: StaticHeaplessMemoryPool<3> =
 | 
			
		||||
                StaticHeaplessMemoryPool::new(true);
 | 
			
		||||
            assert!(
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,8 @@ use crate::pus::source_buffer_large_enough;
 | 
			
		||||
use arbitrary_int::u11;
 | 
			
		||||
use spacepackets::ByteConversionError;
 | 
			
		||||
use spacepackets::SpHeader;
 | 
			
		||||
use spacepackets::ecss::CreatorConfig;
 | 
			
		||||
use spacepackets::ecss::EcssEnumeration;
 | 
			
		||||
use spacepackets::ecss::tm::PusTmCreator;
 | 
			
		||||
use spacepackets::ecss::tm::PusTmSecondaryHeader;
 | 
			
		||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
 | 
			
		||||
use spacepackets::ecss::{CreatorConfig, EcssEnumeration};
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
pub use alloc_mod::*;
 | 
			
		||||
@@ -164,14 +162,14 @@ mod alloc_mod {
 | 
			
		||||
            default_apid: u11,
 | 
			
		||||
            default_dest_id: u16,
 | 
			
		||||
            max_event_id_and_aux_data_size: usize,
 | 
			
		||||
        ) -> Self {
 | 
			
		||||
        ) -> Option<Self> {
 | 
			
		||||
            let reporter = EventReportCreator::new(default_apid, default_dest_id);
 | 
			
		||||
            Self {
 | 
			
		||||
            Some(Self {
 | 
			
		||||
                id,
 | 
			
		||||
                source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
 | 
			
		||||
                report_creator: reporter,
 | 
			
		||||
                tm_hook: DummyEventHook::default(),
 | 
			
		||||
            }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    impl<EventTmHookInstance: EventTmHook> EventReporter<EventTmHookInstance> {
 | 
			
		||||
@@ -181,14 +179,14 @@ mod alloc_mod {
 | 
			
		||||
            default_dest_id: u16,
 | 
			
		||||
            max_event_id_and_aux_data_size: usize,
 | 
			
		||||
            tm_hook: EventTmHookInstance,
 | 
			
		||||
        ) -> Self {
 | 
			
		||||
        ) -> Option<Self> {
 | 
			
		||||
            let reporter = EventReportCreator::new(default_apid, default_dest_id);
 | 
			
		||||
            Self {
 | 
			
		||||
            Some(Self {
 | 
			
		||||
                id,
 | 
			
		||||
                source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
 | 
			
		||||
                report_creator: reporter,
 | 
			
		||||
                tm_hook,
 | 
			
		||||
            }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn event_info(
 | 
			
		||||
@@ -265,7 +263,7 @@ mod alloc_mod {
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::ComponentId;
 | 
			
		||||
    use crate::events_legacy::{EventU32, Severity};
 | 
			
		||||
    use crate::events::{EventU32, Severity};
 | 
			
		||||
    use crate::pus::test_util::TEST_COMPONENT_ID_0;
 | 
			
		||||
    use crate::pus::tests::CommonTmInfo;
 | 
			
		||||
    use crate::pus::{ChannelWithId, EcssTmSender, EcssTmtcError, PusTmVariant};
 | 
			
		||||
@@ -375,12 +373,14 @@ mod tests {
 | 
			
		||||
        error_data: Option<&[u8]>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let mut sender = TestSender::default();
 | 
			
		||||
        let mut reporter = EventReporter::new(
 | 
			
		||||
        let reporter = EventReporter::new(
 | 
			
		||||
            TEST_COMPONENT_ID_0.id(),
 | 
			
		||||
            EXAMPLE_APID,
 | 
			
		||||
            0,
 | 
			
		||||
            max_event_aux_data_buf,
 | 
			
		||||
        );
 | 
			
		||||
        assert!(reporter.is_some());
 | 
			
		||||
        let mut reporter = reporter.unwrap();
 | 
			
		||||
        let time_stamp_empty: [u8; 7] = [0; 7];
 | 
			
		||||
        let mut error_copy = Vec::new();
 | 
			
		||||
        if let Some(err_data) = error_data {
 | 
			
		||||
@@ -471,7 +471,9 @@ mod tests {
 | 
			
		||||
    fn insufficient_buffer() {
 | 
			
		||||
        let mut sender = TestSender::default();
 | 
			
		||||
        for i in 0..3 {
 | 
			
		||||
            let mut reporter = EventReporter::new(0, EXAMPLE_APID, 0, i);
 | 
			
		||||
            let reporter = EventReporter::new(0, EXAMPLE_APID, 0, i);
 | 
			
		||||
            assert!(reporter.is_some());
 | 
			
		||||
            let mut reporter = reporter.unwrap();
 | 
			
		||||
            check_buf_too_small(&mut reporter, &mut sender, i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
use crate::events_legacy::{EventU32, GenericEvent, Severity};
 | 
			
		||||
use crate::events::{EventU32, GenericEvent, Severity};
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
use crate::events_legacy::{EventU32TypedSev, HasSeverity};
 | 
			
		||||
use crate::events::{EventU32TypedSev, HasSeverity};
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
use core::hash::Hash;
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
@@ -100,7 +100,7 @@ pub mod alloc_mod {
 | 
			
		||||
    use core::marker::PhantomData;
 | 
			
		||||
 | 
			
		||||
    use crate::{
 | 
			
		||||
        events_legacy::EventU16,
 | 
			
		||||
        events::EventU16,
 | 
			
		||||
        params::{Params, WritableToBeBytes},
 | 
			
		||||
        pus::event::{DummyEventHook, EventTmHook},
 | 
			
		||||
    };
 | 
			
		||||
@@ -318,7 +318,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::request::UniqueApidTargetId;
 | 
			
		||||
    use crate::{events_legacy::SeverityInfo, tmtc::PacketAsVec};
 | 
			
		||||
    use crate::{events::SeverityInfo, tmtc::PacketAsVec};
 | 
			
		||||
    use std::sync::mpsc::{self, TryRecvError};
 | 
			
		||||
 | 
			
		||||
    const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(1, 0);
 | 
			
		||||
@@ -328,11 +328,13 @@ mod tests {
 | 
			
		||||
    const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
 | 
			
		||||
 | 
			
		||||
    fn create_basic_man_1() -> DefaultPusEventU32TmCreator {
 | 
			
		||||
        let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128);
 | 
			
		||||
        let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
 | 
			
		||||
            .expect("Creating event repoter failed");
 | 
			
		||||
        PusEventTmCreatorWithMap::new_with_default_backend(reporter)
 | 
			
		||||
    }
 | 
			
		||||
    fn create_basic_man_2() -> DefaultPusEventU32TmCreator {
 | 
			
		||||
        let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128);
 | 
			
		||||
        let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
 | 
			
		||||
            .expect("Creating event repoter failed");
 | 
			
		||||
        let backend = DefaultPusEventReportingMap::default();
 | 
			
		||||
        PusEventTmCreatorWithMap::new(reporter, backend)
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::events_legacy::EventU32;
 | 
			
		||||
use crate::events::EventU32;
 | 
			
		||||
use crate::pus::event_man::{EventRequest, EventRequestWithToken};
 | 
			
		||||
use crate::pus::verification::TcStateToken;
 | 
			
		||||
use crate::pus::{DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError};
 | 
			
		||||
@@ -168,7 +168,7 @@ mod tests {
 | 
			
		||||
    use crate::pus::{GenericConversionError, HandlingStatus, MpscTcReceiver};
 | 
			
		||||
    use crate::tmtc::PacketSenderWithSharedPool;
 | 
			
		||||
    use crate::{
 | 
			
		||||
        events_legacy::EventU32,
 | 
			
		||||
        events::EventU32,
 | 
			
		||||
        pus::{
 | 
			
		||||
            DirectPusPacketHandlerResult, EcssTcInSharedPoolCacher, PusPacketHandlingError,
 | 
			
		||||
            event_man::EventRequestWithToken,
 | 
			
		||||
@@ -179,7 +179,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    use super::PusEventServiceHandler;
 | 
			
		||||
 | 
			
		||||
    const TEST_EVENT_0: EventU32 = EventU32::new(crate::events_legacy::Severity::Info, 5, 25);
 | 
			
		||||
    const TEST_EVENT_0: EventU32 = EventU32::new(crate::events::Severity::Info, 5, 25);
 | 
			
		||||
 | 
			
		||||
    struct Pus5HandlerWithStoreTester {
 | 
			
		||||
        common: PusServiceHandlerWithSharedStoreCommon,
 | 
			
		||||
 
 | 
			
		||||
@@ -449,7 +449,7 @@ pub mod alloc_mod {
 | 
			
		||||
    /// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
 | 
			
		||||
    /// The only requirement is that a valid active request information instance and a request
 | 
			
		||||
    /// are returned by the core conversion function. The active request type needs to fulfill
 | 
			
		||||
    /// the [ActiveRequest] trait bound.
 | 
			
		||||
    /// the [ActiveRequestProvider] trait bound.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The user should take care of performing the error handling as well. Some of the following
 | 
			
		||||
    /// aspects might be relevant:
 | 
			
		||||
@@ -1126,7 +1126,7 @@ pub mod std_mod {
 | 
			
		||||
    /// groups (for example individual services).
 | 
			
		||||
    ///
 | 
			
		||||
    /// This base class can handle PUS telecommands backed by different memory storage machanisms
 | 
			
		||||
    /// by using the [CacheAndReadRawEcssTc] abstraction. This object provides some convenience
 | 
			
		||||
    /// by using the [EcssTcInMemConverter] abstraction. This object provides some convenience
 | 
			
		||||
    /// methods to make the generic parts of TC handling easier.
 | 
			
		||||
    pub struct PusServiceHelper<
 | 
			
		||||
        TcReceiver: EcssTcReceiver,
 | 
			
		||||
@@ -1409,7 +1409,7 @@ pub mod tests {
 | 
			
		||||
            let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::sync_channel(10);
 | 
			
		||||
            let (tm_tx, tm_rx) = mpsc::sync_channel(10);
 | 
			
		||||
 | 
			
		||||
            let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
 | 
			
		||||
            let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
 | 
			
		||||
            let verification_handler =
 | 
			
		||||
                VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
 | 
			
		||||
            let test_srv_tm_sender =
 | 
			
		||||
@@ -1502,7 +1502,7 @@ pub mod tests {
 | 
			
		||||
            let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
 | 
			
		||||
            let (tm_tx, tm_rx) = mpsc::channel();
 | 
			
		||||
 | 
			
		||||
            let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
 | 
			
		||||
            let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
 | 
			
		||||
            let verification_handler =
 | 
			
		||||
                VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
 | 
			
		||||
            let in_store_converter = EcssTcVecCacher::default();
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
//! The core data structure of this module is the [PusScheduler]. This structure can be used
 | 
			
		||||
//! to perform the scheduling of telecommands like specified in the ECSS standard.
 | 
			
		||||
use arbitrary_int::{u11, u14};
 | 
			
		||||
use core::fmt::Debug;
 | 
			
		||||
use core::fmt::{Debug, Display, Formatter};
 | 
			
		||||
use core::time::Duration;
 | 
			
		||||
#[cfg(feature = "serde")]
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
@@ -12,6 +12,8 @@ use spacepackets::ecss::tc::{GenericPusTcSecondaryHeader, IsPusTelecommand, PusT
 | 
			
		||||
use spacepackets::ecss::{PusError, PusPacket, WritablePusPacket};
 | 
			
		||||
use spacepackets::time::{CcsdsTimeProvider, TimeReader, TimeWriter, TimestampError, UnixTime};
 | 
			
		||||
use spacepackets::{ByteConversionError, CcsdsPacket};
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
 | 
			
		||||
use crate::pool::{PoolError, PoolProvider};
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
@@ -23,7 +25,6 @@ pub use alloc_mod::*;
 | 
			
		||||
/// the source ID found in the secondary header of PUS telecommands.
 | 
			
		||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub struct RequestId {
 | 
			
		||||
    pub(crate) source_id: u16,
 | 
			
		||||
    pub(crate) apid: u11,
 | 
			
		||||
@@ -56,6 +57,7 @@ impl RequestId {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn as_u64(&self) -> u64 {
 | 
			
		||||
        ((self.source_id as u64) << 32)
 | 
			
		||||
            | ((self.apid.value() as u64) << 16)
 | 
			
		||||
@@ -142,39 +144,107 @@ impl<TimeProvider: CcsdsTimeProvider + Clone> TimeWindow<TimeProvider> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 | 
			
		||||
pub enum ScheduleError {
 | 
			
		||||
    #[error("pus error: {0}")]
 | 
			
		||||
    PusError(#[from] PusError),
 | 
			
		||||
    PusError(PusError),
 | 
			
		||||
    /// The release time is within the time-margin added on top of the current time.
 | 
			
		||||
    /// The first parameter is the current time, the second one the time margin, and the third one
 | 
			
		||||
    /// the release time.
 | 
			
		||||
    #[error("release time in margin")]
 | 
			
		||||
    ReleaseTimeInTimeMargin {
 | 
			
		||||
        current_time: UnixTime,
 | 
			
		||||
        time_margin: Duration,
 | 
			
		||||
        release_time: UnixTime,
 | 
			
		||||
    },
 | 
			
		||||
    /// Nested time-tagged commands are not allowed.
 | 
			
		||||
    #[error("nested scheduled tc")]
 | 
			
		||||
    NestedScheduledTc,
 | 
			
		||||
    #[error("store error")]
 | 
			
		||||
    Pool(#[from] PoolError),
 | 
			
		||||
    #[error("tc data empty")]
 | 
			
		||||
    StoreError(PoolError),
 | 
			
		||||
    TcDataEmpty,
 | 
			
		||||
    #[error("timestamp error: {0}")]
 | 
			
		||||
    TimestampError(#[from] TimestampError),
 | 
			
		||||
    #[error("wrong subservice number {0}")]
 | 
			
		||||
    TimestampError(TimestampError),
 | 
			
		||||
    WrongSubservice(u8),
 | 
			
		||||
    #[error("wrong service number {0}")]
 | 
			
		||||
    WrongService(u8),
 | 
			
		||||
    #[error("byte conversion error: {0}")]
 | 
			
		||||
    ByteConversionError(#[from] ByteConversionError),
 | 
			
		||||
    ByteConversionError(ByteConversionError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for ScheduleError {
 | 
			
		||||
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            ScheduleError::PusError(e) => {
 | 
			
		||||
                write!(f, "Pus Error: {e}")
 | 
			
		||||
            }
 | 
			
		||||
            ScheduleError::ReleaseTimeInTimeMargin {
 | 
			
		||||
                current_time,
 | 
			
		||||
                time_margin,
 | 
			
		||||
                release_time,
 | 
			
		||||
            } => {
 | 
			
		||||
                write!(
 | 
			
		||||
                    f,
 | 
			
		||||
                    "time margin too short, current time: {current_time:?}, time margin: {time_margin:?}, release time: {release_time:?}"
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            ScheduleError::NestedScheduledTc => {
 | 
			
		||||
                write!(f, "nested scheduling is not allowed")
 | 
			
		||||
            }
 | 
			
		||||
            ScheduleError::StoreError(e) => {
 | 
			
		||||
                write!(f, "pus scheduling: {e}")
 | 
			
		||||
            }
 | 
			
		||||
            ScheduleError::TcDataEmpty => {
 | 
			
		||||
                write!(f, "empty TC data field")
 | 
			
		||||
            }
 | 
			
		||||
            ScheduleError::TimestampError(e) => {
 | 
			
		||||
                write!(f, "pus scheduling: {e}")
 | 
			
		||||
            }
 | 
			
		||||
            ScheduleError::WrongService(srv) => {
 | 
			
		||||
                write!(f, "pus scheduling: wrong service number {srv}")
 | 
			
		||||
            }
 | 
			
		||||
            ScheduleError::WrongSubservice(subsrv) => {
 | 
			
		||||
                write!(f, "pus scheduling: wrong subservice number {subsrv}")
 | 
			
		||||
            }
 | 
			
		||||
            ScheduleError::ByteConversionError(e) => {
 | 
			
		||||
                write!(f, "pus scheduling: {e}")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<PusError> for ScheduleError {
 | 
			
		||||
    fn from(e: PusError) -> Self {
 | 
			
		||||
        Self::PusError(e)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<PoolError> for ScheduleError {
 | 
			
		||||
    fn from(e: PoolError) -> Self {
 | 
			
		||||
        Self::StoreError(e)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<TimestampError> for ScheduleError {
 | 
			
		||||
    fn from(e: TimestampError) -> Self {
 | 
			
		||||
        Self::TimestampError(e)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl From<ByteConversionError> for ScheduleError {
 | 
			
		||||
    fn from(e: ByteConversionError) -> Self {
 | 
			
		||||
        Self::ByteConversionError(e)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
impl Error for ScheduleError {
 | 
			
		||||
    fn source(&self) -> Option<&(dyn Error + 'static)> {
 | 
			
		||||
        match self {
 | 
			
		||||
            ScheduleError::PusError(e) => Some(e),
 | 
			
		||||
            ScheduleError::StoreError(e) => Some(e),
 | 
			
		||||
            ScheduleError::TimestampError(e) => Some(e),
 | 
			
		||||
            ScheduleError::ByteConversionError(e) => Some(e),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generic trait for scheduler objects which are able to schedule ECSS PUS C packets.
 | 
			
		||||
pub trait PusScheduler {
 | 
			
		||||
pub trait PusSchedulerProvider {
 | 
			
		||||
    type TimeProvider: CcsdsTimeProvider + TimeReader;
 | 
			
		||||
 | 
			
		||||
    fn reset(&mut self, store: &mut (impl PoolProvider + ?Sized)) -> Result<(), PoolError>;
 | 
			
		||||
@@ -331,7 +401,7 @@ pub mod alloc_mod {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Currently, sub-schedules and groups are not supported.
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    pub struct PusSchedulerAlloc {
 | 
			
		||||
    pub struct PusScheduler {
 | 
			
		||||
        // TODO: Use MonotonicTime from tai-time crate instead of UnixTime and cache leap seconds.
 | 
			
		||||
        // TODO: Introduce optional limit of commands stored in the TC map. If a limit is set,
 | 
			
		||||
        // there will be a check for each insertion whether the map is full, making the memory
 | 
			
		||||
@@ -341,8 +411,7 @@ pub mod alloc_mod {
 | 
			
		||||
        time_margin: Duration,
 | 
			
		||||
        enabled: bool,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl PusSchedulerAlloc {
 | 
			
		||||
    impl PusScheduler {
 | 
			
		||||
        /// Create a new PUS scheduler.
 | 
			
		||||
        ///
 | 
			
		||||
        /// # Arguments
 | 
			
		||||
@@ -354,7 +423,7 @@ pub mod alloc_mod {
 | 
			
		||||
        /// * `tc_buf_size` - Buffer for temporary storage of telecommand packets. This buffer
 | 
			
		||||
        ///   should be large enough to accomodate the largest expected TC packets.
 | 
			
		||||
        pub fn new(init_current_time: UnixTime, time_margin: Duration) -> Self {
 | 
			
		||||
            PusSchedulerAlloc {
 | 
			
		||||
            PusScheduler {
 | 
			
		||||
                tc_map: Default::default(),
 | 
			
		||||
                current_time: init_current_time,
 | 
			
		||||
                time_margin,
 | 
			
		||||
@@ -376,12 +445,10 @@ pub mod alloc_mod {
 | 
			
		||||
            num_entries
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[inline]
 | 
			
		||||
        pub fn update_time(&mut self, current_time: UnixTime) {
 | 
			
		||||
            self.current_time = current_time;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[inline]
 | 
			
		||||
        pub fn current_time(&self) -> &UnixTime {
 | 
			
		||||
            &self.current_time
 | 
			
		||||
        }
 | 
			
		||||
@@ -725,7 +792,7 @@ pub mod alloc_mod {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl PusScheduler for PusSchedulerAlloc {
 | 
			
		||||
    impl PusSchedulerProvider for PusScheduler {
 | 
			
		||||
        type TimeProvider = cds::CdsTime;
 | 
			
		||||
 | 
			
		||||
        /// This will disable the scheduler and clear the schedule as specified in 6.11.4.4.
 | 
			
		||||
@@ -795,8 +862,7 @@ mod tests {
 | 
			
		||||
        PoolAddr, PoolError, PoolProvider, StaticMemoryPool, StaticPoolAddr, StaticPoolConfig,
 | 
			
		||||
    };
 | 
			
		||||
    use alloc::collections::btree_map::Range;
 | 
			
		||||
    use arbitrary_int::traits::Integer;
 | 
			
		||||
    use arbitrary_int::{u11, u14};
 | 
			
		||||
    use arbitrary_int::traits::Integer as _;
 | 
			
		||||
    use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
 | 
			
		||||
    use spacepackets::ecss::{CreatorConfig, WritablePusPacket};
 | 
			
		||||
    use spacepackets::time::{TimeWriter, UnixTime, cds};
 | 
			
		||||
@@ -806,6 +872,8 @@ mod tests {
 | 
			
		||||
    #[allow(unused_imports)]
 | 
			
		||||
    use std::{println, vec};
 | 
			
		||||
 | 
			
		||||
    const ZERO_SEQ: u14 = u14::ZERO;
 | 
			
		||||
 | 
			
		||||
    fn pus_tc_base(timestamp: UnixTime, buf: &mut [u8]) -> (SpHeader, usize) {
 | 
			
		||||
        let cds_time =
 | 
			
		||||
            cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent)
 | 
			
		||||
@@ -881,8 +949,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_enable_api() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        assert!(scheduler.is_enabled());
 | 
			
		||||
        scheduler.disable();
 | 
			
		||||
        assert!(!scheduler.is_enabled());
 | 
			
		||||
@@ -896,8 +963,7 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
@@ -939,8 +1005,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn insert_multi_with_same_time() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(
 | 
			
		||||
@@ -952,7 +1017,7 @@ mod tests {
 | 
			
		||||
                    }),
 | 
			
		||||
                    RequestId {
 | 
			
		||||
                        seq_count: u14::new(1),
 | 
			
		||||
                        apid: u11::ZERO,
 | 
			
		||||
                        apid: u11::new(0),
 | 
			
		||||
                        source_id: 0,
 | 
			
		||||
                    },
 | 
			
		||||
                ),
 | 
			
		||||
@@ -999,8 +1064,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_time_update() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let time = UnixTime::new(1, 2_000_000);
 | 
			
		||||
        scheduler.update_time(time);
 | 
			
		||||
        assert_eq!(scheduler.current_time(), &time);
 | 
			
		||||
@@ -1030,22 +1094,20 @@ mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_request_id() {
 | 
			
		||||
        let src_id_to_set = 12;
 | 
			
		||||
        let apid_to_set = u11::new(0x22);
 | 
			
		||||
        let seq_count = u14::new(105);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(apid_to_set, u14::new(105), 0);
 | 
			
		||||
        let apid_to_set = 0x22;
 | 
			
		||||
        let seq_count = 105;
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(u11::new(apid_to_set), u14::new(105), 0);
 | 
			
		||||
        let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
 | 
			
		||||
        sec_header.source_id = src_id_to_set;
 | 
			
		||||
        let ping_tc =
 | 
			
		||||
            PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
 | 
			
		||||
        let req_id = RequestId::from_tc(&ping_tc);
 | 
			
		||||
        assert_eq!(req_id.source_id(), src_id_to_set);
 | 
			
		||||
        assert_eq!(req_id.apid(), apid_to_set);
 | 
			
		||||
        assert_eq!(req_id.seq_count(), seq_count);
 | 
			
		||||
        assert_eq!(req_id.apid().value(), apid_to_set);
 | 
			
		||||
        assert_eq!(req_id.seq_count().value(), seq_count);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            req_id.as_u64(),
 | 
			
		||||
            ((src_id_to_set as u64) << 32)
 | 
			
		||||
                | (apid_to_set.value() as u64) << 16
 | 
			
		||||
                | seq_count.value() as u64
 | 
			
		||||
            ((src_id_to_set as u64) << 32) | (apid_to_set as u64) << 16 | seq_count as u64
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -1054,11 +1116,10 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
@@ -1123,11 +1184,10 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
@@ -1184,13 +1244,12 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        scheduler.disable();
 | 
			
		||||
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
@@ -1250,15 +1309,14 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn insert_unwrapped_tc() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
 | 
			
		||||
        let info = scheduler
 | 
			
		||||
            .insert_unwrapped_tc(
 | 
			
		||||
@@ -1273,7 +1331,7 @@ mod tests {
 | 
			
		||||
        let mut read_buf: [u8; 64] = [0; 64];
 | 
			
		||||
        pool.read(&tc_info_0.addr(), &mut read_buf).unwrap();
 | 
			
		||||
        let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
 | 
			
		||||
        assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::ZERO, &[]));
 | 
			
		||||
        assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::new(0), &[]));
 | 
			
		||||
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 1);
 | 
			
		||||
 | 
			
		||||
@@ -1301,8 +1359,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn insert_wrapped_tc() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
@@ -1324,7 +1381,7 @@ mod tests {
 | 
			
		||||
        let read_len = pool.read(&info.addr, &mut buf).unwrap();
 | 
			
		||||
        let check_tc = PusTcReader::new(&buf).expect("incorrect Pus tc raw data");
 | 
			
		||||
        assert_eq!(read_len, check_tc.packet_len());
 | 
			
		||||
        assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::ZERO, &[]));
 | 
			
		||||
        assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::new(0), &[]));
 | 
			
		||||
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 1);
 | 
			
		||||
 | 
			
		||||
@@ -1354,8 +1411,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn insert_wrong_service() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
@@ -1380,8 +1436,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn insert_wrong_subservice() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
@@ -1406,8 +1461,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn insert_wrapped_tc_faulty_app_data() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
@@ -1424,8 +1478,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn insert_doubly_wrapped_time_tagged_cmd() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
@@ -1443,7 +1496,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_ctor_from_current() {
 | 
			
		||||
        let scheduler = PusSchedulerAlloc::new_with_current_init_time(Duration::from_secs(5))
 | 
			
		||||
        let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
 | 
			
		||||
            .expect("creation from current time failed");
 | 
			
		||||
        let current_time = scheduler.current_time;
 | 
			
		||||
        assert!(current_time.as_secs() > 0);
 | 
			
		||||
@@ -1451,8 +1504,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_update_from_current() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        assert_eq!(scheduler.current_time.as_secs(), 0);
 | 
			
		||||
        scheduler
 | 
			
		||||
            .update_time_from_now()
 | 
			
		||||
@@ -1462,8 +1514,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn release_time_within_time_margin() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
@@ -1496,10 +1547,9 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
            .expect("insertion failed");
 | 
			
		||||
@@ -1534,10 +1584,9 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
            .expect("insertion failed");
 | 
			
		||||
@@ -1561,10 +1610,9 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
            .expect("inserting tc failed");
 | 
			
		||||
@@ -1583,10 +1631,9 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
            .expect("inserting tc failed");
 | 
			
		||||
@@ -1605,10 +1652,9 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
            .expect("inserting tc failed");
 | 
			
		||||
@@ -1648,8 +1694,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn insert_full_store_test() {
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(1, 64)],
 | 
			
		||||
@@ -1665,7 +1710,7 @@ mod tests {
 | 
			
		||||
        assert!(insert_res.is_err());
 | 
			
		||||
        let err = insert_res.unwrap_err();
 | 
			
		||||
        match err {
 | 
			
		||||
            ScheduleError::Pool(e) => match e {
 | 
			
		||||
            ScheduleError::StoreError(e) => match e {
 | 
			
		||||
                PoolError::StoreFull(_) => {}
 | 
			
		||||
                _ => panic!("unexpected store error {e}"),
 | 
			
		||||
            },
 | 
			
		||||
@@ -1675,7 +1720,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    fn insert_command_with_release_time(
 | 
			
		||||
        pool: &mut StaticMemoryPool,
 | 
			
		||||
        scheduler: &mut PusSchedulerAlloc,
 | 
			
		||||
        scheduler: &mut PusScheduler,
 | 
			
		||||
        seq_count: u14,
 | 
			
		||||
        release_secs: u64,
 | 
			
		||||
    ) -> TcInfo {
 | 
			
		||||
@@ -1694,10 +1739,11 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
        let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let tc_info_0 =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 50);
 | 
			
		||||
        let tc_info_1 =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 100);
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 2);
 | 
			
		||||
        let check_range = |range: Range<UnixTime, Vec<TcInfo>>| {
 | 
			
		||||
            let mut tcs_in_range = 0;
 | 
			
		||||
@@ -1727,11 +1773,12 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
        let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
        let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 50);
 | 
			
		||||
        let tc_info_1 =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 100);
 | 
			
		||||
        let tc_info_2 =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 150);
 | 
			
		||||
        let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
 | 
			
		||||
            &UnixTime::new_only_secs(100),
 | 
			
		||||
            cds::SubmillisPrecision::Absent,
 | 
			
		||||
@@ -1763,11 +1810,12 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
        let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
        let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let tc_info_0 =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 50);
 | 
			
		||||
        let tc_info_1 =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 100);
 | 
			
		||||
        let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::new(0), 150);
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 3);
 | 
			
		||||
 | 
			
		||||
        let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
 | 
			
		||||
@@ -1799,12 +1847,11 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
        let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
        let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
 | 
			
		||||
        let _ = insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 200);
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let _ = insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
 | 
			
		||||
        let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
 | 
			
		||||
        let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 150);
 | 
			
		||||
        let _ = insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 200);
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 4);
 | 
			
		||||
 | 
			
		||||
        let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
 | 
			
		||||
@@ -1841,10 +1888,9 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 2);
 | 
			
		||||
        let del_res = scheduler.delete_all(&mut pool);
 | 
			
		||||
        assert!(del_res.is_ok());
 | 
			
		||||
@@ -1853,8 +1899,8 @@ mod tests {
 | 
			
		||||
        // Contrary to reset, this does not disable the scheduler.
 | 
			
		||||
        assert!(scheduler.is_enabled());
 | 
			
		||||
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 2);
 | 
			
		||||
        let del_res = scheduler
 | 
			
		||||
            .delete_by_time_filter(TimeWindow::<cds::CdsTime>::new_select_all(), &mut pool);
 | 
			
		||||
@@ -1871,13 +1917,12 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
 | 
			
		||||
        let cmd_0_to_delete =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
 | 
			
		||||
        let cmd_1_to_delete =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 150);
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 3);
 | 
			
		||||
        let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
 | 
			
		||||
            &UnixTime::new_only_secs(100),
 | 
			
		||||
@@ -1899,13 +1944,12 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let cmd_0_to_delete =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
 | 
			
		||||
        let cmd_1_to_delete =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
 | 
			
		||||
        insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 150);
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 3);
 | 
			
		||||
 | 
			
		||||
        let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
 | 
			
		||||
@@ -1928,16 +1972,15 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let cmd_out_of_range_0 =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 50);
 | 
			
		||||
        let cmd_0_to_delete =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 100);
 | 
			
		||||
        let cmd_1_to_delete =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 150);
 | 
			
		||||
        let cmd_out_of_range_1 =
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 200);
 | 
			
		||||
            insert_command_with_release_time(&mut pool, &mut scheduler, ZERO_SEQ, 200);
 | 
			
		||||
        assert_eq!(scheduler.num_scheduled_telecommands(), 4);
 | 
			
		||||
 | 
			
		||||
        let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
 | 
			
		||||
@@ -1967,11 +2010,10 @@ mod tests {
 | 
			
		||||
            vec![(10, 32), (5, 64)],
 | 
			
		||||
            false,
 | 
			
		||||
        ));
 | 
			
		||||
        let mut scheduler =
 | 
			
		||||
            PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
        let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
 | 
			
		||||
 | 
			
		||||
        let mut buf: [u8; 32] = [0; 32];
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
 | 
			
		||||
        let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
 | 
			
		||||
 | 
			
		||||
        scheduler
 | 
			
		||||
            .insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use super::scheduler::PusScheduler;
 | 
			
		||||
use super::scheduler::PusSchedulerProvider;
 | 
			
		||||
use super::verification::{VerificationReporter, VerificationReportingProvider};
 | 
			
		||||
use super::{
 | 
			
		||||
    CacheAndReadRawEcssTc, DirectPusPacketHandlerResult, EcssTcInSharedPoolCacher, EcssTcReceiver,
 | 
			
		||||
@@ -26,11 +26,11 @@ pub struct PusSchedServiceHandler<
 | 
			
		||||
    TmSender: EcssTmSender,
 | 
			
		||||
    TcInMemConverter: CacheAndReadRawEcssTc,
 | 
			
		||||
    VerificationReporter: VerificationReportingProvider,
 | 
			
		||||
    PusSchedulerInstance: PusScheduler,
 | 
			
		||||
    PusScheduler: PusSchedulerProvider,
 | 
			
		||||
> {
 | 
			
		||||
    pub service_helper:
 | 
			
		||||
        PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
 | 
			
		||||
    scheduler: PusSchedulerInstance,
 | 
			
		||||
    scheduler: PusScheduler,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<
 | 
			
		||||
@@ -38,7 +38,7 @@ impl<
 | 
			
		||||
    TmSender: EcssTmSender,
 | 
			
		||||
    TcInMemConverter: CacheAndReadRawEcssTc,
 | 
			
		||||
    VerificationReporter: VerificationReportingProvider,
 | 
			
		||||
    Scheduler: PusScheduler,
 | 
			
		||||
    Scheduler: PusSchedulerProvider,
 | 
			
		||||
> PusSchedServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter, Scheduler>
 | 
			
		||||
{
 | 
			
		||||
    pub fn new(
 | 
			
		||||
@@ -254,13 +254,13 @@ mod tests {
 | 
			
		||||
    use crate::pus::{DirectPusPacketHandlerResult, MpscTcReceiver, PusPacketHandlingError};
 | 
			
		||||
    use crate::pus::{
 | 
			
		||||
        EcssTcInSharedPoolCacher,
 | 
			
		||||
        scheduler::{self, PusScheduler, TcInfo},
 | 
			
		||||
        scheduler::{self, PusSchedulerProvider, TcInfo},
 | 
			
		||||
        tests::PusServiceHandlerWithSharedStoreCommon,
 | 
			
		||||
        verification::{RequestId, TcStateAccepted, VerificationToken},
 | 
			
		||||
    };
 | 
			
		||||
    use crate::tmtc::PacketSenderWithSharedPool;
 | 
			
		||||
    use alloc::collections::VecDeque;
 | 
			
		||||
    use arbitrary_int::traits::Integer as _;
 | 
			
		||||
    use arbitrary_int::traits::Integer;
 | 
			
		||||
    use arbitrary_int::u14;
 | 
			
		||||
    use delegate::delegate;
 | 
			
		||||
    use spacepackets::SpHeader;
 | 
			
		||||
@@ -349,7 +349,7 @@ mod tests {
 | 
			
		||||
        inserted_tcs: VecDeque<TcInfo>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl PusScheduler for TestScheduler {
 | 
			
		||||
    impl PusSchedulerProvider for TestScheduler {
 | 
			
		||||
        type TimeProvider = cds::CdsTime;
 | 
			
		||||
 | 
			
		||||
        fn reset(
 | 
			
		||||
@@ -446,7 +446,7 @@ mod tests {
 | 
			
		||||
        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::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        reply_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(1), 0);
 | 
			
		||||
        sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8);
 | 
			
		||||
        let enable_scheduling = PusTcCreator::new(
 | 
			
		||||
            reply_header,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ use crate::pus::{
 | 
			
		||||
    DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError, PusTmVariant,
 | 
			
		||||
};
 | 
			
		||||
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
 | 
			
		||||
use arbitrary_int::traits::Integer as _;
 | 
			
		||||
use arbitrary_int::u14;
 | 
			
		||||
use spacepackets::SpHeader;
 | 
			
		||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
 | 
			
		||||
@@ -79,7 +78,7 @@ impl<
 | 
			
		||||
            // the unchecked API here.
 | 
			
		||||
            let reply_header = SpHeader::new_for_unseg_tm(
 | 
			
		||||
                self.service_helper.verif_reporter().apid(),
 | 
			
		||||
                u14::ZERO,
 | 
			
		||||
                u14::new(0),
 | 
			
		||||
                0,
 | 
			
		||||
            );
 | 
			
		||||
            let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp);
 | 
			
		||||
@@ -154,7 +153,6 @@ mod tests {
 | 
			
		||||
        PartialPusHandlingError, PusPacketHandlingError,
 | 
			
		||||
    };
 | 
			
		||||
    use crate::tmtc::PacketSenderWithSharedPool;
 | 
			
		||||
    use arbitrary_int::traits::Integer as _;
 | 
			
		||||
    use arbitrary_int::u14;
 | 
			
		||||
    use delegate::delegate;
 | 
			
		||||
    use spacepackets::SpHeader;
 | 
			
		||||
@@ -291,7 +289,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    fn ping_test(test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler)) {
 | 
			
		||||
        // Create a ping TC, verify acceptance.
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
 | 
			
		||||
        let ping_tc =
 | 
			
		||||
            PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
 | 
			
		||||
@@ -347,7 +345,7 @@ mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_sending_unsupported_service() {
 | 
			
		||||
        let mut test_harness = Pus17HandlerWithStoreTester::new(0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(3, 1);
 | 
			
		||||
        let ping_tc =
 | 
			
		||||
            PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
 | 
			
		||||
@@ -369,7 +367,7 @@ mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_sending_custom_subservice() {
 | 
			
		||||
        let mut test_harness = Pus17HandlerWithStoreTester::new(0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
 | 
			
		||||
        let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0), 0);
 | 
			
		||||
        let sec_header = PusTcSecondaryHeader::new_simple(17, 200);
 | 
			
		||||
        let ping_tc =
 | 
			
		||||
            PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
 | 
			
		||||
 
 | 
			
		||||
@@ -20,15 +20,15 @@
 | 
			
		||||
//!     VerificationReportingProvider, VerificationReporterConfig, VerificationReporter
 | 
			
		||||
//! };
 | 
			
		||||
//! use satrs::tmtc::{SharedStaticMemoryPool, PacketSenderWithSharedPool};
 | 
			
		||||
//! use satrs::spacepackets::seq_count::SeqCountProviderSimple;
 | 
			
		||||
//! use satrs::request::UniqueApidTargetId;
 | 
			
		||||
//! use spacepackets::ecss::PusPacket;
 | 
			
		||||
//! use spacepackets::SpHeader;
 | 
			
		||||
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader, CreatorConfig};
 | 
			
		||||
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
 | 
			
		||||
//! use spacepackets::ecss::tm::PusTmReader;
 | 
			
		||||
//! use arbitrary_int::u11;
 | 
			
		||||
//!
 | 
			
		||||
//! const EMPTY_STAMP: [u8; 7] = [0; 7];
 | 
			
		||||
//! const TEST_APID: u11 = u11::new(0x02);
 | 
			
		||||
//! const TEST_APID: u16 = 0x02;
 | 
			
		||||
//! const TEST_COMPONENT_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
 | 
			
		||||
//!
 | 
			
		||||
//! let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
@@ -38,14 +38,14 @@
 | 
			
		||||
//! let shared_tm_pool = SharedStaticMemoryPool::new(RwLock::new(tm_pool));
 | 
			
		||||
//! let (verif_tx, verif_rx) = mpsc::sync_channel(10);
 | 
			
		||||
//! let sender = PacketSenderWithSharedPool::new_with_shared_packet_pool(verif_tx, &shared_tm_pool);
 | 
			
		||||
//! let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
 | 
			
		||||
//! let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
 | 
			
		||||
//! let mut  reporter = VerificationReporter::new(TEST_COMPONENT_ID.id(), &cfg);
 | 
			
		||||
//!
 | 
			
		||||
//! let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
 | 
			
		||||
//! let pus_tc_0 = PusTcCreator::new_no_app_data(
 | 
			
		||||
//!     SpHeader::new_from_apid(TEST_APID),
 | 
			
		||||
//!     tc_header,
 | 
			
		||||
//!     CreatorConfig::default()
 | 
			
		||||
//!     true
 | 
			
		||||
//! );
 | 
			
		||||
//! let init_token = reporter.start_verification(&pus_tc_0);
 | 
			
		||||
//!
 | 
			
		||||
@@ -82,6 +82,7 @@
 | 
			
		||||
//! context involving multiple threads
 | 
			
		||||
use crate::params::{Params, WritableToBeBytes};
 | 
			
		||||
use crate::pus::{EcssTmSender, EcssTmtcError, source_buffer_large_enough};
 | 
			
		||||
use arbitrary_int::traits::Integer as _;
 | 
			
		||||
use arbitrary_int::{u3, u11, u14};
 | 
			
		||||
use core::fmt::{Debug, Display, Formatter};
 | 
			
		||||
use core::hash::{Hash, Hasher};
 | 
			
		||||
@@ -91,14 +92,13 @@ use core::mem::size_of;
 | 
			
		||||
use delegate::delegate;
 | 
			
		||||
#[cfg(feature = "serde")]
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use spacepackets::SpHeader;
 | 
			
		||||
use spacepackets::ecss::tc::IsPusTelecommand;
 | 
			
		||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
 | 
			
		||||
use spacepackets::ecss::{CreatorConfig, EcssEnumeration};
 | 
			
		||||
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceControl};
 | 
			
		||||
use spacepackets::{MAX_APID, SpHeader};
 | 
			
		||||
 | 
			
		||||
pub use spacepackets::ecss::verification::*;
 | 
			
		||||
pub use spacepackets::seq_count::SequenceCounterSimple;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
pub use alloc_mod::*;
 | 
			
		||||
@@ -158,18 +158,16 @@ impl RequestId {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn raw(&self) -> u32 {
 | 
			
		||||
        ((self.version_number.value() as u32) << 29)
 | 
			
		||||
        ((self.version_number.as_u32()) << 29)
 | 
			
		||||
            | ((self.packet_id.raw() as u32) << 16)
 | 
			
		||||
            | self.psc.raw() as u32
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn packet_id(&self) -> PacketId {
 | 
			
		||||
    pub fn packet_id(&self) -> PacketId {
 | 
			
		||||
        self.packet_id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn packet_seq_ctrl(&self) -> PacketSequenceControl {
 | 
			
		||||
    pub fn packet_sequence_control(&self) -> PacketSequenceControl {
 | 
			
		||||
        self.psc
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -835,7 +833,7 @@ impl VerificationReportCreator {
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "alloc")]
 | 
			
		||||
pub mod alloc_mod {
 | 
			
		||||
    use arbitrary_int::traits::Integer as _;
 | 
			
		||||
    use arbitrary_int::u11;
 | 
			
		||||
    use spacepackets::ecss::PusError;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
@@ -856,13 +854,16 @@ pub mod alloc_mod {
 | 
			
		||||
            step_field_width: usize,
 | 
			
		||||
            fail_code_field_width: usize,
 | 
			
		||||
            max_fail_data_len: usize,
 | 
			
		||||
        ) -> Self {
 | 
			
		||||
            Self {
 | 
			
		||||
        ) -> Option<Self> {
 | 
			
		||||
            if apid > MAX_APID {
 | 
			
		||||
                return None;
 | 
			
		||||
            }
 | 
			
		||||
            Some(Self {
 | 
			
		||||
                apid,
 | 
			
		||||
                step_field_width,
 | 
			
		||||
                fail_code_field_width,
 | 
			
		||||
                max_fail_data_len,
 | 
			
		||||
            }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -874,7 +875,7 @@ pub mod alloc_mod {
 | 
			
		||||
        fn modify_tm(&self, tm: &mut PusTmCreator);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// [VerificationHook] which does nothing. This is the default hook variant for
 | 
			
		||||
    /// [VerificationHookProvider] which does nothing. This is the default hook variant for
 | 
			
		||||
    /// the [VerificationReporter], assuming that any necessary packet manipulation is performed by
 | 
			
		||||
    /// a centralized TM funnel or inlet.
 | 
			
		||||
    #[derive(Default, Copy, Clone)]
 | 
			
		||||
@@ -920,7 +921,7 @@ pub mod alloc_mod {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<VerificationHookInstance: VerificationHook> VerificationReporter<VerificationHookInstance> {
 | 
			
		||||
        /// The provided [VerificationHook] can be used to modify a verification packet
 | 
			
		||||
        /// The provided [VerificationHookProvider] can be used to modify a verification packet
 | 
			
		||||
        /// before it is sent.
 | 
			
		||||
        pub fn new_with_hook(
 | 
			
		||||
            owner_id: ComponentId,
 | 
			
		||||
@@ -1006,7 +1007,7 @@ pub mod alloc_mod {
 | 
			
		||||
                .acceptance_success(
 | 
			
		||||
                    source_data_buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    time_stamp,
 | 
			
		||||
                )
 | 
			
		||||
@@ -1029,7 +1030,7 @@ pub mod alloc_mod {
 | 
			
		||||
                .acceptance_failure(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    params,
 | 
			
		||||
                )
 | 
			
		||||
@@ -1054,7 +1055,7 @@ pub mod alloc_mod {
 | 
			
		||||
                .start_success(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    time_stamp,
 | 
			
		||||
                )
 | 
			
		||||
@@ -1080,7 +1081,7 @@ pub mod alloc_mod {
 | 
			
		||||
                .start_failure(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    params,
 | 
			
		||||
                )
 | 
			
		||||
@@ -1106,7 +1107,7 @@ pub mod alloc_mod {
 | 
			
		||||
                .step_success(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    time_stamp,
 | 
			
		||||
                    step,
 | 
			
		||||
@@ -1130,7 +1131,7 @@ pub mod alloc_mod {
 | 
			
		||||
            let mut buf = self.source_data_buf.borrow_mut();
 | 
			
		||||
            let mut tm_creator = self
 | 
			
		||||
                .reporter_creator
 | 
			
		||||
                .step_failure(buf.as_mut_slice(), token, u14::ZERO, 0, params)
 | 
			
		||||
                .step_failure(buf.as_mut_slice(), token, u14::new(0), 0, params)
 | 
			
		||||
                .map_err(PusError::ByteConversion)?;
 | 
			
		||||
            self.tm_hook.modify_tm(&mut tm_creator);
 | 
			
		||||
            sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
 | 
			
		||||
@@ -1154,7 +1155,7 @@ pub mod alloc_mod {
 | 
			
		||||
                .completion_success(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    time_stamp,
 | 
			
		||||
                )
 | 
			
		||||
@@ -1180,7 +1181,7 @@ pub mod alloc_mod {
 | 
			
		||||
                .completion_failure(
 | 
			
		||||
                    buf.as_mut_slice(),
 | 
			
		||||
                    &token.request_id(),
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    params,
 | 
			
		||||
                )
 | 
			
		||||
@@ -1333,7 +1334,7 @@ pub fn handle_step_failure_with_generic_params(
 | 
			
		||||
#[cfg(any(feature = "test_util", test))]
 | 
			
		||||
pub mod test_util {
 | 
			
		||||
    use alloc::vec::Vec;
 | 
			
		||||
    use arbitrary_int::traits::Integer;
 | 
			
		||||
    use arbitrary_int::u11;
 | 
			
		||||
    use core::cell::RefCell;
 | 
			
		||||
    use std::collections::VecDeque;
 | 
			
		||||
 | 
			
		||||
@@ -1395,7 +1396,7 @@ pub mod test_util {
 | 
			
		||||
        fn set_apid(&mut self, _apid: Apid) {}
 | 
			
		||||
 | 
			
		||||
        fn apid(&self) -> Apid {
 | 
			
		||||
            Apid::ZERO
 | 
			
		||||
            u11::new(0)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn acceptance_success(
 | 
			
		||||
@@ -1727,13 +1728,13 @@ pub mod tests {
 | 
			
		||||
    use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
 | 
			
		||||
    use alloc::format;
 | 
			
		||||
    use alloc::string::ToString;
 | 
			
		||||
    use arbitrary_int::traits::Integer;
 | 
			
		||||
    use arbitrary_int::{u11, u14};
 | 
			
		||||
    use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
 | 
			
		||||
    use spacepackets::ecss::{
 | 
			
		||||
        CreatorConfig, EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumeration, PusError, PusPacket,
 | 
			
		||||
        WritablePusPacket,
 | 
			
		||||
    };
 | 
			
		||||
    use spacepackets::seq_count::SequenceCounterSimple;
 | 
			
		||||
    use spacepackets::util::UnsignedEnum;
 | 
			
		||||
    use spacepackets::{ByteConversionError, SpHeader};
 | 
			
		||||
    use std::cell::RefCell;
 | 
			
		||||
@@ -1743,8 +1744,8 @@ pub mod tests {
 | 
			
		||||
    use std::vec::Vec;
 | 
			
		||||
 | 
			
		||||
    use super::{
 | 
			
		||||
        DummyVerificationHook, FailParamHelper, SequenceCounterSimple, TcStateAccepted,
 | 
			
		||||
        TcStateStarted, VerificationHook, VerificationReportingProvider, WasAtLeastAccepted,
 | 
			
		||||
        DummyVerificationHook, FailParamHelper, TcStateAccepted, TcStateStarted, VerificationHook,
 | 
			
		||||
        VerificationReportingProvider, WasAtLeastAccepted,
 | 
			
		||||
        handle_completion_failure_with_generic_params,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -1832,7 +1833,7 @@ pub mod tests {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn base_reporter(id: ComponentId, max_fail_data_len: usize) -> VerificationReporter {
 | 
			
		||||
        let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, max_fail_data_len);
 | 
			
		||||
        let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, max_fail_data_len).unwrap();
 | 
			
		||||
        VerificationReporter::new(id, &cfg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1840,7 +1841,7 @@ pub mod tests {
 | 
			
		||||
        id: ComponentId,
 | 
			
		||||
        hook: VerificationHookInstance,
 | 
			
		||||
    ) -> VerificationReporter<VerificationHookInstance> {
 | 
			
		||||
        let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
 | 
			
		||||
        let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
 | 
			
		||||
        VerificationReporter::new_with_hook(id, &cfg, hook)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1942,7 +1943,7 @@ pub mod tests {
 | 
			
		||||
                common: CommonTmInfo::new(
 | 
			
		||||
                    1,
 | 
			
		||||
                    TEST_APID,
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    self.reporter.dest_id(),
 | 
			
		||||
                    timestamp,
 | 
			
		||||
@@ -2009,7 +2010,7 @@ pub mod tests {
 | 
			
		||||
                common: CommonTmInfo::new(
 | 
			
		||||
                    2,
 | 
			
		||||
                    TEST_APID,
 | 
			
		||||
                    u14::ZERO,
 | 
			
		||||
                    u14::new(0),
 | 
			
		||||
                    0,
 | 
			
		||||
                    self.reporter.dest_id(),
 | 
			
		||||
                    timestamp,
 | 
			
		||||
@@ -2274,7 +2275,7 @@ pub mod tests {
 | 
			
		||||
            .expect("step 1 failed");
 | 
			
		||||
        assert_eq!(testbench.sender.service_queue.borrow().len(), 4);
 | 
			
		||||
        testbench.check_acceptance_success(&EMPTY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::ZERO, 0, &EMPTY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::new(0), 0, &EMPTY_STAMP);
 | 
			
		||||
        testbench.check_step_success(0, &EMPTY_STAMP);
 | 
			
		||||
        testbench.check_step_success(1, &EMPTY_STAMP);
 | 
			
		||||
    }
 | 
			
		||||
@@ -2308,7 +2309,7 @@ pub mod tests {
 | 
			
		||||
            .step_failure(started_token, fail_params)
 | 
			
		||||
            .expect("Step failure failed");
 | 
			
		||||
        testbench.check_acceptance_success(&EMPTY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::new(0), 0, DUMMY_STAMP);
 | 
			
		||||
        testbench.check_step_success(0, &EMPTY_STAMP);
 | 
			
		||||
        testbench.check_step_failure(&fail_step, &fail_code, &fail_data_raw);
 | 
			
		||||
    }
 | 
			
		||||
@@ -2330,7 +2331,7 @@ pub mod tests {
 | 
			
		||||
            .completion_failure(started_token, fail_params)
 | 
			
		||||
            .expect("Completion failure");
 | 
			
		||||
        testbench.check_acceptance_success(&EMPTY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::new(0), 0, DUMMY_STAMP);
 | 
			
		||||
 | 
			
		||||
        testbench.check_completion_failure(&fail_code, &[]);
 | 
			
		||||
    }
 | 
			
		||||
@@ -2350,8 +2351,8 @@ pub mod tests {
 | 
			
		||||
            .completion_success(started_token, &EMPTY_STAMP)
 | 
			
		||||
            .expect("Sending completion success failed");
 | 
			
		||||
        testbench.check_acceptance_success(&EMPTY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
 | 
			
		||||
        testbench.check_completion_success(u14::ZERO, 0);
 | 
			
		||||
        testbench.check_start_success(u14::new(0), 0, DUMMY_STAMP);
 | 
			
		||||
        testbench.check_completion_success(u14::new(0), 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -2432,7 +2433,7 @@ pub mod tests {
 | 
			
		||||
        );
 | 
			
		||||
        assert!(result.unwrap());
 | 
			
		||||
        testbench.check_acceptance_success(&EMPTY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::ZERO, 0, &EMPTY_STAMP);
 | 
			
		||||
        testbench.check_start_success(u14::new(0), 0, &EMPTY_STAMP);
 | 
			
		||||
        testbench.check_step_failure(&step, &fail_code, fail_data.as_bytes());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use arbitrary_int::u11;
 | 
			
		||||
use arbitrary_int::{traits::Integer as _, u11};
 | 
			
		||||
use core::{fmt, marker::PhantomData};
 | 
			
		||||
#[cfg(feature = "serde")]
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
@@ -23,7 +23,7 @@ use crate::{
 | 
			
		||||
/// for them. This can be useful for tasks like tracking their progress.
 | 
			
		||||
pub type RequestId = u32;
 | 
			
		||||
 | 
			
		||||
/// CCSDS APID type definition. Please note that the APID is a 14 bit value.
 | 
			
		||||
/// CCSDS APID type definition. Please note that the APID is a 11 bit value.
 | 
			
		||||
pub type Apid = u11;
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 | 
			
		||||
@@ -41,7 +41,7 @@ impl UniqueApidTargetId {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn raw(&self) -> ComponentId {
 | 
			
		||||
        ((self.apid.value() as u64) << 32) | (self.unique_id as u64)
 | 
			
		||||
        ((self.apid.as_u64()) << 32) | (self.unique_id as u64)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn id(&self) -> ComponentId {
 | 
			
		||||
@@ -496,6 +496,10 @@ pub mod std_mod {
 | 
			
		||||
mod tests {
 | 
			
		||||
    use std::sync::mpsc;
 | 
			
		||||
 | 
			
		||||
    use crate::{
 | 
			
		||||
        queue::{GenericReceiveError, GenericSendError},
 | 
			
		||||
        request::{MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
 | 
			
		||||
    };
 | 
			
		||||
    use alloc::string::ToString;
 | 
			
		||||
    use arbitrary_int::{u11, u14};
 | 
			
		||||
    use spacepackets::{
 | 
			
		||||
@@ -506,11 +510,6 @@ mod tests {
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    use crate::{
 | 
			
		||||
        queue::{GenericReceiveError, GenericSendError},
 | 
			
		||||
        request::{Apid, MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};
 | 
			
		||||
 | 
			
		||||
    const TEST_CHANNEL_ID_0: u64 = 1;
 | 
			
		||||
@@ -519,7 +518,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_basic_target_id_with_apid() {
 | 
			
		||||
        let id = UniqueApidTargetId::new(Apid::new(0x111), 0x01);
 | 
			
		||||
        let id = UniqueApidTargetId::new(u11::new(0x111), 0x01);
 | 
			
		||||
        assert_eq!(id.apid.value(), 0x111);
 | 
			
		||||
        assert_eq!(id.unique_id, 0x01);
 | 
			
		||||
        assert_eq!(id.id(), id.raw());
 | 
			
		||||
 
 | 
			
		||||
@@ -62,10 +62,9 @@ impl PusTmWithCdsShortHelper {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use arbitrary_int::{u11, u14};
 | 
			
		||||
    use spacepackets::{CcsdsPacket, ecss::PusPacket, time::cds::CdsTime};
 | 
			
		||||
 | 
			
		||||
    use super::PusTmWithCdsShortHelper;
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_helper_with_stamper() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								satrs/tests/pus_autogen_events.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								satrs/tests/pus_autogen_events.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
#![allow(dead_code, unused_imports)]
 | 
			
		||||
 | 
			
		||||
use satrs::events::{
 | 
			
		||||
    EventU32, EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw,
 | 
			
		||||
    Severity, SeverityInfo, SeverityLow, SeverityMedium,
 | 
			
		||||
};
 | 
			
		||||
use std::convert::AsRef;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct GroupIdIntrospection {
 | 
			
		||||
    name: &'static str,
 | 
			
		||||
    id: LargestGroupIdRaw,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct EventIntrospection {
 | 
			
		||||
    name: &'static str,
 | 
			
		||||
    group_id: GroupIdIntrospection,
 | 
			
		||||
    event: &'static EventU32,
 | 
			
		||||
    info: &'static str,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//#[event(descr="This is some info event")]
 | 
			
		||||
const INFO_EVENT_0: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(0, 0);
 | 
			
		||||
const INFO_EVENT_0_ERASED: EventU32 = EventU32::const_from_info(INFO_EVENT_0);
 | 
			
		||||
 | 
			
		||||
// This is ideally auto-generated
 | 
			
		||||
const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection {
 | 
			
		||||
    name: "INFO_EVENT_0",
 | 
			
		||||
    group_id: GroupIdIntrospection {
 | 
			
		||||
        id: 0,
 | 
			
		||||
        name: "Group ID 0 without name",
 | 
			
		||||
    },
 | 
			
		||||
    event: &INFO_EVENT_0_ERASED,
 | 
			
		||||
    info: "This is some info event",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//#[event(descr="This is some low severity event")]
 | 
			
		||||
const SOME_LOW_SEV_EVENT: EventU32TypedSev<SeverityLow> = EventU32TypedSev::new(0, 12);
 | 
			
		||||
 | 
			
		||||
//const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT];
 | 
			
		||||
 | 
			
		||||
//#[event_group]
 | 
			
		||||
const TEST_GROUP_NAME: u16 = 1;
 | 
			
		||||
// Auto-generated?
 | 
			
		||||
const TEST_GROUP_NAME_NAME: &str = "TEST_GROUP_NAME";
 | 
			
		||||
 | 
			
		||||
//#[event(desc="Some medium severity event")]
 | 
			
		||||
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: EventU32TypedSev<SeverityMedium> =
 | 
			
		||||
    EventU32TypedSev::new(TEST_GROUP_NAME, 0);
 | 
			
		||||
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED: EventU32 =
 | 
			
		||||
    EventU32::const_from_medium(MEDIUM_SEV_EVENT_IN_OTHER_GROUP);
 | 
			
		||||
 | 
			
		||||
// Also auto-generated
 | 
			
		||||
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION: EventIntrospection = EventIntrospection {
 | 
			
		||||
    name: "MEDIUM_SEV_EVENT_IN_OTHER_GROUP",
 | 
			
		||||
    group_id: GroupIdIntrospection {
 | 
			
		||||
        name: TEST_GROUP_NAME_NAME,
 | 
			
		||||
        id: TEST_GROUP_NAME,
 | 
			
		||||
    },
 | 
			
		||||
    event: &MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED,
 | 
			
		||||
    info: "Some medium severity event",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CONST_SLICE: &[u8] = &[0, 1, 2, 3];
 | 
			
		||||
const INTROSPECTION_FOR_TEST_GROUP_0: [&EventIntrospection; 2] =
 | 
			
		||||
    [&INFO_EVENT_0_INTROSPECTION, &INFO_EVENT_0_INTROSPECTION];
 | 
			
		||||
 | 
			
		||||
//const INTROSPECTION_FOR_TABLE: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_0;
 | 
			
		||||
 | 
			
		||||
const INTROSPECTION_FOR_TEST_GROUP_NAME: [&EventIntrospection; 1] =
 | 
			
		||||
    [&MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION];
 | 
			
		||||
//const BLAH: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_NAME;
 | 
			
		||||
 | 
			
		||||
const ALL_EVENTS: [&[&EventIntrospection]; 2] = [
 | 
			
		||||
    &INTROSPECTION_FOR_TEST_GROUP_0,
 | 
			
		||||
    &INTROSPECTION_FOR_TEST_GROUP_NAME,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn main() {
 | 
			
		||||
    //let test = stringify!(INFO_EVENT);
 | 
			
		||||
    //println!("{:?}", test);
 | 
			
		||||
    //for event in EVENT_LIST {
 | 
			
		||||
    //    println!("{:?}", event);
 | 
			
		||||
    //}
 | 
			
		||||
    //for events in ALL_EVENTS.into_iter().flatten() {
 | 
			
		||||
    //    dbg!("{:?}", events);
 | 
			
		||||
    //}
 | 
			
		||||
    //for introspection_info in INTROSPECTION_FOR_TEST_GROUP {
 | 
			
		||||
    //    dbg!("{:?}", introspection_info);
 | 
			
		||||
    //}
 | 
			
		||||
    //let test_struct =
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
use arbitrary_int::u11;
 | 
			
		||||
use satrs::event_man_legacy::{
 | 
			
		||||
use satrs::event_man::{
 | 
			
		||||
    EventManagerWithMpsc, EventMessage, EventMessageU32, EventRoutingError, EventSendProvider,
 | 
			
		||||
    EventU32SenderMpsc,
 | 
			
		||||
};
 | 
			
		||||
use satrs::events_legacy::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
 | 
			
		||||
use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
 | 
			
		||||
use satrs::params::U32Pair;
 | 
			
		||||
use satrs::params::{Params, ParamsHeapless, WritableToBeBytes};
 | 
			
		||||
use satrs::pus::event_man::{DefaultPusEventReportingMap, EventReporter, PusEventTmCreatorWithMap};
 | 
			
		||||
@@ -36,7 +36,8 @@ fn test_threaded_usage() {
 | 
			
		||||
    event_man.subscribe_all(pus_event_man_send_provider.target_id());
 | 
			
		||||
    event_man.add_sender(pus_event_man_send_provider);
 | 
			
		||||
    let (event_packet_tx, event_packet_rx) = mpsc::channel::<PacketAsVec>();
 | 
			
		||||
    let reporter = EventReporter::new(TEST_ID.raw(), u11::new(0x02), 0, 128);
 | 
			
		||||
    let reporter = EventReporter::new(TEST_ID.raw(), u11::new(0x02), 0, 128)
 | 
			
		||||
        .expect("Creating event reporter failed");
 | 
			
		||||
    let pus_event_man =
 | 
			
		||||
        PusEventTmCreatorWithMap::new(reporter, DefaultPusEventReportingMap::default());
 | 
			
		||||
    let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| {
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ pub mod crossbeam_test {
 | 
			
		||||
        // We use a synced sequence count provider here because both verification reporters have the
 | 
			
		||||
        // the same APID. If they had distinct APIDs, the more correct approach would be to have
 | 
			
		||||
        // each reporter have an own sequence count provider.
 | 
			
		||||
        let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
 | 
			
		||||
        let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
 | 
			
		||||
        // Shared pool object to store the verification PUS telemetry
 | 
			
		||||
        let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
 | 
			
		||||
            vec![(10, 32), (10, 64), (10, 128), (10, 1024)],
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ use std::{
 | 
			
		||||
    thread,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use arbitrary_int::{u11, u14};
 | 
			
		||||
use arbitrary_int::{traits::Integer, u11, u14};
 | 
			
		||||
use hashbrown::HashSet;
 | 
			
		||||
use satrs::{
 | 
			
		||||
    ComponentId,
 | 
			
		||||
@@ -268,7 +268,7 @@ fn test_ccsds_server() {
 | 
			
		||||
        .expect("setting reas timeout failed");
 | 
			
		||||
 | 
			
		||||
    // Send ping telecommand.
 | 
			
		||||
    let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, u14::new(0), 0);
 | 
			
		||||
    let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, u14::ZERO, 0);
 | 
			
		||||
    let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
 | 
			
		||||
    let tc_0 = ping_tc.to_vec().expect("packet creation failed");
 | 
			
		||||
    stream
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user