Compare commits
5 Commits
bump-space
...
ccsds-sche
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7b8a8c1d1
|
||
| 06e713a557 | |||
|
|
778512d50e | ||
| 5d40638964 | |||
|
|
1b07b845f2 |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -47,6 +47,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- run: cargo fmt --all -- --check
|
||||
|
||||
docs:
|
||||
@@ -55,7 +57,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc -p satrs --all-features
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
@@ -63,4 +65,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- run: cargo clippy -- -D warnings
|
||||
|
||||
23
justfile
Normal file
23
justfile
Normal file
@@ -0,0 +1,23 @@
|
||||
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
|
||||
@@ -21,6 +21,8 @@ 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"
|
||||
|
||||
@@ -36,8 +38,8 @@ version = "0.1.1"
|
||||
path = "../satrs-mib"
|
||||
|
||||
[features]
|
||||
heap_tmtc = []
|
||||
default = ["heap_tmtc"]
|
||||
heap_tmtc = []
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.11"
|
||||
|
||||
@@ -574,7 +574,7 @@ mod tests {
|
||||
let (request_tx, request_rx) = mpsc::sync_channel(5);
|
||||
let (reply_tx_to_pus, reply_rx_to_pus) = mpsc::sync_channel(5);
|
||||
let (reply_tx_to_parent, reply_rx_to_parent) = mpsc::sync_channel(5);
|
||||
let id = UniqueApidTargetId::new(Apid::Acs as u16, 1);
|
||||
let id = UniqueApidTargetId::new(Apid::Acs.raw_value(), 1);
|
||||
let mode_node = ModeRequestHandlerMpscBounded::new(id.into(), request_rx);
|
||||
let (composite_request_tx, composite_request_rx) = mpsc::channel();
|
||||
let (hk_reply_tx, hk_reply_rx) = mpsc::sync_channel(10);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use arbitrary_int::u11;
|
||||
use satrs::pus::verification::RequestId;
|
||||
use satrs::spacepackets::ecss::tc::PusTcCreator;
|
||||
use satrs::spacepackets::ecss::tm::PusTmReader;
|
||||
use satrs::{spacepackets::ecss::PusPacket, spacepackets::SpHeader};
|
||||
use satrs::spacepackets::ecss::CreatorConfig;
|
||||
use satrs::spacepackets::SpHeader;
|
||||
use satrs_example::config::{OBSW_SERVER_ADDR, SERVER_PORT};
|
||||
use std::net::{IpAddr, SocketAddr, UdpSocket};
|
||||
use std::time::Duration;
|
||||
@@ -9,7 +11,13 @@ use std::time::Duration;
|
||||
fn main() {
|
||||
let mut buf = [0; 32];
|
||||
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
||||
let pus_tc = PusTcCreator::new_simple(SpHeader::new_from_apid(0x02), 17, 1, &[], true);
|
||||
let pus_tc = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(u11::new(0x02)),
|
||||
17,
|
||||
1,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed");
|
||||
let tc_req_id = RequestId::new(&pus_tc);
|
||||
println!("Packing and sending PUS ping command TC[17,1] with request ID {tc_req_id}");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use arbitrary_int::u11;
|
||||
use lazy_static::lazy_static;
|
||||
use satrs::{
|
||||
res_code::ResultU16,
|
||||
@@ -10,7 +11,7 @@ use strum::IntoEnumIterator;
|
||||
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use satrs::{
|
||||
events::{EventU32TypedSev, SeverityInfo},
|
||||
events_legacy::{EventU32TypedSev, SeverityInfo},
|
||||
pool::{StaticMemoryPool, StaticPoolConfig},
|
||||
};
|
||||
|
||||
@@ -44,7 +45,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, id as u16));
|
||||
set.insert(PacketId::new(PacketType::Tc, true, u11::new(id as u16)));
|
||||
}
|
||||
set
|
||||
};
|
||||
|
||||
@@ -572,7 +572,7 @@ mod tests {
|
||||
let (switch_request_tx, switch_reqest_rx) = mpsc::channel();
|
||||
let shared_switch_map = Arc::new(Mutex::new(SwitchSet::default()));
|
||||
let mut handler = PcduHandler::new(
|
||||
UniqueApidTargetId::new(Apid::Eps as u16, 0),
|
||||
UniqueApidTargetId::new(Apid::Eps.raw_value(), 0),
|
||||
"TEST_PCDU",
|
||||
mode_node,
|
||||
composite_request_rx,
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use std::sync::mpsc::{self};
|
||||
|
||||
use crate::pus::create_verification_reporter;
|
||||
use satrs::event_man::{EventMessageU32, EventRoutingError};
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
use arbitrary_int::u11;
|
||||
use satrs::event_man_legacy::{EventMessageU32, EventRoutingError};
|
||||
use satrs::pus::event::EventTmHook;
|
||||
use satrs::pus::verification::VerificationReporter;
|
||||
use satrs::pus::EcssTmSender;
|
||||
use satrs::request::UniqueApidTargetId;
|
||||
use satrs::{
|
||||
event_man::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
|
||||
event_man_legacy::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
|
||||
pus::{
|
||||
event_man::{
|
||||
DefaultPusEventU32TmCreator, EventReporter, EventRequest, EventRequestWithToken,
|
||||
@@ -23,7 +25,7 @@ use crate::update_time;
|
||||
// This helper sets the APID of the event sender for the PUS telemetry.
|
||||
#[derive(Default)]
|
||||
pub struct EventApidSetter {
|
||||
pub next_apid: u16,
|
||||
pub next_apid: u11,
|
||||
}
|
||||
|
||||
impl EventTmHook for EventApidSetter {
|
||||
@@ -59,12 +61,11 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
|
||||
// telemetry for each event.
|
||||
let event_reporter = EventReporter::new_with_hook(
|
||||
PUS_EVENT_MANAGEMENT.raw(),
|
||||
0,
|
||||
u11::ZERO,
|
||||
0,
|
||||
128,
|
||||
EventApidSetter::default(),
|
||||
)
|
||||
.unwrap();
|
||||
);
|
||||
let pus_event_dispatcher =
|
||||
DefaultPusEventU32TmCreator::new_with_default_backend(event_reporter);
|
||||
let pus_event_man_send_provider = EventU32SenderMpscBounded::new(
|
||||
@@ -218,19 +219,16 @@ impl<TmSender: EcssTmSender> EventHandler<TmSender> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use satrs::{
|
||||
events::EventU32,
|
||||
events_legacy::EventU32,
|
||||
pus::verification::VerificationReporterConfig,
|
||||
spacepackets::{
|
||||
ecss::{tm::PusTmReader, PusPacket},
|
||||
CcsdsPacket,
|
||||
},
|
||||
spacepackets::ecss::{tm::PusTmReader, PusPacket},
|
||||
tmtc::PacketAsVec,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(1, 2);
|
||||
const TEST_EVENT: EventU32 = EventU32::new(satrs::events::Severity::Info, 1, 1);
|
||||
const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(u11::new(1), 2);
|
||||
const TEST_EVENT: EventU32 = EventU32::new(satrs::events_legacy::Severity::Info, 1, 1);
|
||||
|
||||
pub struct EventManagementTestbench {
|
||||
pub event_tx: mpsc::SyncSender<EventMessageU32>,
|
||||
@@ -244,7 +242,7 @@ 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(0x05, 2, 2, 128).unwrap();
|
||||
let verif_reporter_cfg = VerificationReporterConfig::new(u11::new(0x05), 2, 2, 128);
|
||||
let verif_reporter =
|
||||
VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg);
|
||||
let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
|
||||
@@ -270,7 +268,7 @@ mod tests {
|
||||
.event_tx
|
||||
.send(EventMessageU32::new(
|
||||
TEST_CREATOR_ID.id(),
|
||||
EventU32::new(satrs::events::Severity::Info, 1, 1),
|
||||
EventU32::new(satrs::events_legacy::Severity::Info, 1, 1),
|
||||
))
|
||||
.expect("failed to send event");
|
||||
testbench.pus_event_handler.handle_event_requests();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use derive_new::new;
|
||||
use satrs::hk::UniqueId;
|
||||
use satrs::request::UniqueApidTargetId;
|
||||
use satrs::spacepackets::ecss::hk;
|
||||
use satrs::spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||
use satrs::spacepackets::ecss::{hk, CreatorConfig};
|
||||
use satrs::spacepackets::{ByteConversionError, SpHeader};
|
||||
|
||||
#[derive(Debug, new, Copy, Clone)]
|
||||
@@ -63,7 +63,7 @@ impl PusHkHelper {
|
||||
SpHeader::new_from_apid(self.component_id.apid),
|
||||
sec_header,
|
||||
&buf[0..8 + hk_data_len],
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! This is an auto-generated configuration module.
|
||||
use satrs::request::UniqueApidTargetId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, strum::EnumIter)]
|
||||
#[derive(Debug, PartialEq, Eq, strum::EnumIter)]
|
||||
#[bitbybit::bitenum(u11)]
|
||||
pub enum Apid {
|
||||
Sched = 1,
|
||||
GenericPus = 2,
|
||||
@@ -12,6 +13,7 @@ pub enum Apid {
|
||||
}
|
||||
|
||||
pub mod acs {
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Id {
|
||||
Subsystem = 1,
|
||||
@@ -21,13 +23,13 @@ pub mod acs {
|
||||
}
|
||||
|
||||
pub const SUBSYSTEM: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Acs as u16, Id::Subsystem as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Subsystem as u32);
|
||||
pub const ASSEMBLY: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Acs as u16, Id::Assembly as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Assembly as u32);
|
||||
pub const MGM0: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Acs as u16, Id::Mgm0 as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Mgm0 as u32);
|
||||
pub const MGM1: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Acs as u16, Id::Mgm1 as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Acs.raw_value(), Id::Mgm1 as u32);
|
||||
}
|
||||
|
||||
pub mod eps {
|
||||
@@ -38,9 +40,9 @@ pub mod eps {
|
||||
}
|
||||
|
||||
pub const PCDU: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Eps as u16, Id::Pcdu as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Eps.raw_value(), Id::Pcdu as u32);
|
||||
pub const SUBSYSTEM: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Eps as u16, Id::Subsystem as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Eps.raw_value(), Id::Subsystem as u32);
|
||||
}
|
||||
|
||||
pub mod generic_pus {
|
||||
@@ -55,19 +57,19 @@ pub mod generic_pus {
|
||||
}
|
||||
|
||||
pub const PUS_EVENT_MANAGEMENT: super::UniqueApidTargetId = super::UniqueApidTargetId::new(
|
||||
super::Apid::GenericPus as u16,
|
||||
super::Apid::GenericPus.raw_value(),
|
||||
Id::PusEventManagement as u32,
|
||||
);
|
||||
pub const PUS_ROUTING: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus as u16, Id::PusRouting as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusRouting as u32);
|
||||
pub const PUS_TEST: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus as u16, Id::PusTest as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusTest as u32);
|
||||
pub const PUS_ACTION: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus as u16, Id::PusAction as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusAction as u32);
|
||||
pub const PUS_MODE: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus as u16, Id::PusMode as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusMode as u32);
|
||||
pub const PUS_HK: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus as u16, Id::PusHk as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::GenericPus.raw_value(), Id::PusHk as u32);
|
||||
}
|
||||
|
||||
pub mod sched {
|
||||
@@ -77,7 +79,7 @@ pub mod sched {
|
||||
}
|
||||
|
||||
pub const PUS_SCHED: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Sched as u16, Id::PusSched as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Sched.raw_value(), Id::PusSched as u32);
|
||||
}
|
||||
|
||||
pub mod tmtc {
|
||||
@@ -88,7 +90,7 @@ pub mod tmtc {
|
||||
}
|
||||
|
||||
pub const UDP_SERVER: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Tmtc as u16, Id::UdpServer as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Tmtc.raw_value(), Id::UdpServer as u32);
|
||||
pub const TCP_SERVER: super::UniqueApidTargetId =
|
||||
super::UniqueApidTargetId::new(super::Apid::Tmtc as u16, Id::TcpServer as u32);
|
||||
super::UniqueApidTargetId::new(super::Apid::Tmtc.raw_value(), Id::TcpServer as u32);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#![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, PacketInPool, StoreAndSendError};
|
||||
use satrs::{
|
||||
hal::std::udp_server::{ReceiveResult, UdpTcServer},
|
||||
pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
|
||||
};
|
||||
use satrs::tmtc::{PacketAsVec, StoreAndSendError};
|
||||
|
||||
use satrs::pool::{PoolProviderWithGuards, SharedStaticMemoryPool};
|
||||
use satrs::tmtc::PacketInPool;
|
||||
|
||||
use crate::tmtc::sender::TmTcSender;
|
||||
|
||||
@@ -15,7 +16,6 @@ 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,
|
||||
@@ -113,6 +113,9 @@ mod tests {
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
use arbitrary_int::u14;
|
||||
use satrs::spacepackets::ecss::CreatorConfig;
|
||||
use satrs::{
|
||||
spacepackets::{
|
||||
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||
@@ -177,8 +180,8 @@ mod tests {
|
||||
udp_tc_server,
|
||||
tm_handler,
|
||||
};
|
||||
let sph = SpHeader::new_for_unseg_tc(ids::Apid::GenericPus as u16, 0, 0);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true)
|
||||
let sph = SpHeader::new_for_unseg_tc(ids::Apid::GenericPus.raw_value(), u14::ZERO, 0);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default())
|
||||
.to_vec()
|
||||
.unwrap();
|
||||
let client = UdpSocket::bind("127.0.0.1:0").expect("Connecting to UDP server failed");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use satrs::spacepackets::time::{cds::CdsTime, TimeWriter};
|
||||
use satrs::spacepackets::time::cds::CdsTime;
|
||||
|
||||
pub mod config;
|
||||
pub mod ids;
|
||||
|
||||
@@ -35,7 +35,7 @@ use satrs::{
|
||||
mode_tree::connect_mode_nodes,
|
||||
pus::{event_man::EventRequestWithToken, EcssTcCacher, HandlingStatus},
|
||||
request::{GenericMessage, MessageMetadata},
|
||||
spacepackets::time::{cds::CdsTime, TimeWriter},
|
||||
spacepackets::time::cds::CdsTime,
|
||||
};
|
||||
use satrs_example::{
|
||||
config::{
|
||||
|
||||
@@ -276,6 +276,7 @@ mod tests {
|
||||
use satrs::pus::verification::test_util::TestVerificationReporter;
|
||||
use satrs::pus::{verification, EcssTcVecCacher};
|
||||
use satrs::request::MessageMetadata;
|
||||
use satrs::spacepackets::ecss::CreatorConfig;
|
||||
use satrs::tmtc::PacketAsVec;
|
||||
use satrs::ComponentId;
|
||||
use satrs::{
|
||||
@@ -453,7 +454,8 @@ mod tests {
|
||||
let mut app_data: [u8; 8] = [0; 8];
|
||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_1.to_be_bytes());
|
||||
app_data[4..8].copy_from_slice(&action_id.to_be_bytes());
|
||||
let pus8_packet = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
||||
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);
|
||||
@@ -499,7 +501,7 @@ mod tests {
|
||||
SpHeader::new_from_apid(TEST_APID),
|
||||
sec_header,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
testbench.add_tc(&pus8_packet);
|
||||
let time_stamp: [u8; 7] = [0; 7];
|
||||
@@ -525,7 +527,7 @@ mod tests {
|
||||
SpHeader::new_from_apid(TEST_APID),
|
||||
sec_header,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let token = testbench.add_tc(&pus8_packet);
|
||||
let result = testbench.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0);
|
||||
@@ -564,7 +566,7 @@ mod tests {
|
||||
SpHeader::new_from_apid(TEST_APID),
|
||||
sec_header,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let token = testbench.add_tc(&pus8_packet);
|
||||
let result = testbench.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0);
|
||||
|
||||
@@ -302,10 +302,13 @@ impl TargetedPusService for HkServiceWrapper {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
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,
|
||||
};
|
||||
use satrs::request::MessageMetadata;
|
||||
use satrs::spacepackets::ecss::CreatorConfig;
|
||||
use satrs::{
|
||||
hk::HkRequestVariant,
|
||||
pus::test_util::TEST_APID,
|
||||
@@ -328,7 +331,7 @@ mod tests {
|
||||
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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let target_id = TEST_UNIQUE_ID_0;
|
||||
let unique_id = 5_u32;
|
||||
let mut app_data: [u8; 8] = [0; 8];
|
||||
@@ -340,7 +343,7 @@ mod tests {
|
||||
3,
|
||||
Subservice::TcGenerateOneShotHk as u8,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let accepted_token = hk_bench.add_tc(&hk_req);
|
||||
let (_active_req, req) = hk_bench
|
||||
@@ -358,7 +361,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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let target_id = TEST_UNIQUE_ID_0;
|
||||
let unique_id = 5_u32;
|
||||
let mut app_data: [u8; 8] = [0; 8];
|
||||
@@ -380,7 +383,7 @@ mod tests {
|
||||
3,
|
||||
Subservice::TcEnableHkGeneration as u8,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
generic_check(&tc0);
|
||||
let tc1 = PusTcCreator::new_simple(
|
||||
@@ -388,7 +391,7 @@ mod tests {
|
||||
3,
|
||||
Subservice::TcEnableDiagGeneration as u8,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
generic_check(&tc1);
|
||||
}
|
||||
@@ -397,7 +400,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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let target_id = TEST_UNIQUE_ID_0;
|
||||
let unique_id = 5_u32;
|
||||
let mut app_data: [u8; 8] = [0; 8];
|
||||
@@ -419,7 +422,7 @@ mod tests {
|
||||
3,
|
||||
Subservice::TcDisableHkGeneration as u8,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
generic_check(&tc0);
|
||||
let tc1 = PusTcCreator::new_simple(
|
||||
@@ -427,7 +430,7 @@ mod tests {
|
||||
3,
|
||||
Subservice::TcDisableDiagGeneration as u8,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
generic_check(&tc1);
|
||||
}
|
||||
@@ -436,7 +439,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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let target_id = TEST_UNIQUE_ID_0;
|
||||
let unique_id = 5_u32;
|
||||
let mut app_data: [u8; 12] = [0; 12];
|
||||
@@ -462,7 +465,7 @@ mod tests {
|
||||
3,
|
||||
Subservice::TcModifyHkCollectionInterval as u8,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
generic_check(&tc0);
|
||||
let tc1 = PusTcCreator::new_simple(
|
||||
@@ -470,7 +473,7 @@ mod tests {
|
||||
3,
|
||||
Subservice::TcModifyDiagCollectionInterval as u8,
|
||||
&app_data,
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
generic_check(&tc1);
|
||||
}
|
||||
|
||||
@@ -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).unwrap();
|
||||
let verif_cfg = VerificationReporterConfig::new(apid, 1, 2, 8);
|
||||
// Every software component which needs to generate verification telemetry, gets a cloned
|
||||
// verification reporter.
|
||||
VerificationReporter::new(owner_id, &verif_cfg)
|
||||
@@ -531,9 +531,11 @@ pub fn generic_pus_request_timeout_handler(
|
||||
pub(crate) mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use arbitrary_int::u11;
|
||||
use satrs::pus::test_util::TEST_COMPONENT_ID_0;
|
||||
use satrs::pus::{MpscTmAsVecSender, PusTmVariant};
|
||||
use satrs::request::RequestId;
|
||||
use satrs::spacepackets::ecss::CreatorConfig;
|
||||
use satrs::{
|
||||
pus::{
|
||||
verification::test_util::TestVerificationReporter, ActivePusRequestStd,
|
||||
@@ -590,7 +592,7 @@ pub(crate) mod tests {
|
||||
|
||||
pub fn add_tc(
|
||||
&mut self,
|
||||
apid: u16,
|
||||
apid: u11,
|
||||
apid_target: u32,
|
||||
time_stamp: &[u8],
|
||||
) -> (verification::RequestId, ActivePusRequestStd) {
|
||||
@@ -600,7 +602,7 @@ pub(crate) mod tests {
|
||||
sp_header,
|
||||
sec_header_dummy,
|
||||
&[],
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
));
|
||||
let accepted = self
|
||||
.verif_reporter
|
||||
@@ -719,7 +721,7 @@ pub(crate) mod tests {
|
||||
&mut self,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
time_stamp: &[u8],
|
||||
expected_apid: u16,
|
||||
expected_apid: u11,
|
||||
expected_apid_target: u32,
|
||||
) -> Result<(ActiveRequestInfo, Request), Converter::Error> {
|
||||
if self.current_packet.is_none() {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
use arbitrary_int::u14;
|
||||
use derive_new::new;
|
||||
use satrs::mode_tree::{ModeNode, ModeParent};
|
||||
use satrs::spacepackets::ecss::CreatorConfig;
|
||||
use satrs_example::ids;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
@@ -77,10 +80,15 @@ 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(), 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tm(req_id.packet_id().apid(), u14::ZERO, 0);
|
||||
let sec_header =
|
||||
PusTmSecondaryHeader::new(200, Subservice::TmModeReply as u8, 0, 0, time_stamp);
|
||||
let pus_tm = PusTmCreator::new(sp_header, sec_header, &source_data, true);
|
||||
let pus_tm = PusTmCreator::new(
|
||||
sp_header,
|
||||
sec_header,
|
||||
&source_data,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
tm_sender.send_tm(self.owner_id, PusTmVariant::Direct(pus_tm))?;
|
||||
verification_handler.completion_success(tm_sender, started_token, time_stamp)?;
|
||||
}
|
||||
@@ -290,8 +298,11 @@ 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;
|
||||
use satrs::spacepackets::ecss::CreatorConfig;
|
||||
use satrs::{
|
||||
mode::{ModeAndSubmode, ModeReply, ModeRequest},
|
||||
pus::mode::Subservice,
|
||||
@@ -314,11 +325,11 @@ 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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 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());
|
||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||
let token = testbench.add_tc(&tc);
|
||||
let (_active_req, req) = testbench
|
||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||
@@ -330,7 +341,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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 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);
|
||||
@@ -338,7 +349,7 @@ mod tests {
|
||||
mode_and_submode
|
||||
.write_to_be_bytes(&mut app_data[4..])
|
||||
.unwrap();
|
||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||
let token = testbench.add_tc(&tc);
|
||||
let (_active_req, req) = testbench
|
||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||
@@ -356,11 +367,11 @@ 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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 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());
|
||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||
let token = testbench.add_tc(&tc);
|
||||
let (_active_req, req) = testbench
|
||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||
@@ -372,12 +383,12 @@ 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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let sec_header =
|
||||
PusTcSecondaryHeader::new_simple(200, Subservice::TcAnnounceModeRecursive as u8);
|
||||
let mut app_data: [u8; 4] = [0; 4];
|
||||
app_data[0..4].copy_from_slice(&TEST_UNIQUE_ID_0.to_be_bytes());
|
||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
||||
let tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||
let token = testbench.add_tc(&tc);
|
||||
let (_active_req, req) = testbench
|
||||
.convert(token, &[], TEST_APID, TEST_UNIQUE_ID_0)
|
||||
|
||||
@@ -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::{PusScheduler, TcInfo};
|
||||
use satrs::pus::scheduler::{PusSchedulerAlloc, 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,
|
||||
PusScheduler,
|
||||
PusSchedulerAlloc,
|
||||
>,
|
||||
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 = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
|
||||
let scheduler = PusSchedulerAlloc::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::{EventMessage, EventMessageU32};
|
||||
use satrs::event_man_legacy::{EventMessage, EventMessageU32};
|
||||
use satrs::pus::test::PusService17TestHandler;
|
||||
use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider};
|
||||
use satrs::pus::PartialPusHandlingError;
|
||||
|
||||
@@ -3,18 +3,17 @@ use std::{
|
||||
sync::mpsc::{self},
|
||||
};
|
||||
|
||||
use arbitrary_int::{u11, u14};
|
||||
use log::info;
|
||||
use satrs::{
|
||||
pool::PoolProvider,
|
||||
spacepackets::{
|
||||
ecss::{tm::PusTmZeroCopyWriter, PusPacket},
|
||||
seq_count::CcsdsSimpleSeqCountProvider,
|
||||
seq_count::SequenceCounter,
|
||||
seq_count::SequenceCounterCcsdsSimple,
|
||||
time::cds::MIN_CDS_FIELD_LEN,
|
||||
CcsdsPacket,
|
||||
},
|
||||
};
|
||||
use satrs::{
|
||||
spacepackets::seq_count::SequenceCountProvider,
|
||||
tmtc::{PacketAsVec, PacketInPool, SharedPacketPool},
|
||||
};
|
||||
|
||||
@@ -22,14 +21,16 @@ use crate::interface::tcp::SyncTcpTmSource;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CcsdsSeqCounterMap {
|
||||
apid_seq_counter_map: HashMap<u16, CcsdsSimpleSeqCountProvider>,
|
||||
apid_seq_counter_map: HashMap<u11, SequenceCounterCcsdsSimple>,
|
||||
}
|
||||
impl CcsdsSeqCounterMap {
|
||||
pub fn get_and_increment(&mut self, apid: u16) -> u16 {
|
||||
self.apid_seq_counter_map
|
||||
.entry(apid)
|
||||
.or_default()
|
||||
.get_and_increment()
|
||||
pub fn get_and_increment(&mut self, apid: u11) -> u14 {
|
||||
u14::new(
|
||||
self.apid_seq_counter_map
|
||||
.entry(apid)
|
||||
.or_default()
|
||||
.get_and_increment(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +115,7 @@ impl TmSinkStatic {
|
||||
let mut tm_copy = Vec::new();
|
||||
pool_guard
|
||||
.modify(&pus_tm_in_pool.store_addr, |buf| {
|
||||
let zero_copy_writer = PusTmZeroCopyWriter::new(buf, MIN_CDS_FIELD_LEN)
|
||||
let zero_copy_writer = PusTmZeroCopyWriter::new(buf, MIN_CDS_FIELD_LEN, true)
|
||||
.expect("Creating TM zero copy writer failed");
|
||||
self.common.apply_packet_processing(zero_copy_writer);
|
||||
tm_copy = buf.to_vec()
|
||||
@@ -154,8 +155,9 @@ impl TmSinkDynamic {
|
||||
if let Ok(mut tm) = self.tm_funnel_rx.recv() {
|
||||
// Read the TM, set sequence counter and message counter, and finally update
|
||||
// the CRC.
|
||||
let zero_copy_writer = PusTmZeroCopyWriter::new(&mut tm.packet, MIN_CDS_FIELD_LEN)
|
||||
.expect("Creating TM zero copy writer failed");
|
||||
let zero_copy_writer =
|
||||
PusTmZeroCopyWriter::new(&mut tm.packet, MIN_CDS_FIELD_LEN, true)
|
||||
.expect("Creating TM zero copy writer failed");
|
||||
self.common.apply_packet_processing(zero_copy_writer);
|
||||
self.common.sync_tm_tcp_source.add_tm(&tm.packet);
|
||||
self.tm_server_tx
|
||||
|
||||
@@ -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.15", default-features = false }
|
||||
spacepackets = { version = ">=0.14, <=0.16", default-features = false }
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
|
||||
@@ -8,7 +8,10 @@ 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
|
||||
|
||||
@@ -219,7 +222,8 @@ docs-rs hotfix
|
||||
|
||||
Initial release.
|
||||
|
||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/sat-rs/compare/satrs-v0.3.0-alpha.2...HEAD
|
||||
[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
|
||||
[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,7 +14,7 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
|
||||
|
||||
[dependencies]
|
||||
satrs-shared = { version = "0.2", path = "../satrs-shared" }
|
||||
spacepackets = { version = ">=0.14, <=0.15", default-features = false }
|
||||
spacepackets = { version = "0.16", default-features = false }
|
||||
|
||||
delegate = ">0.7, <=0.13"
|
||||
paste = "1"
|
||||
@@ -24,14 +24,16 @@ 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", optional = true }
|
||||
dyn-clone = { version = "1", optional = true }
|
||||
static_cell = { version = "2" }
|
||||
heapless = { version = "0.9", optional = true }
|
||||
dyn-clone = { version = "1", 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 }
|
||||
|
||||
@@ -47,7 +49,7 @@ tempfile = "3"
|
||||
version = "1"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
default = ["std", "heapless"]
|
||||
std = [
|
||||
"downcast-rs/std",
|
||||
"alloc",
|
||||
@@ -69,7 +71,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
satrs/src/ccsds/mod.rs
Normal file
1
satrs/src/ccsds/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod scheduler;
|
||||
129
satrs/src/ccsds/scheduler.rs
Normal file
129
satrs/src/ccsds/scheduler.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
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::{CcsdsPacket, SpHeader};
|
||||
use spacepackets::SpHeader;
|
||||
|
||||
use crate::{ComponentId, tmtc::PacketSenderRaw};
|
||||
|
||||
@@ -63,7 +63,7 @@ pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
||||
let sp_header = SpHeader::from_be_bytes(&buf[current_idx..]).unwrap().0;
|
||||
match packet_validator.validate(&sp_header, &buf[current_idx..]) {
|
||||
SpValidity::Valid => {
|
||||
let packet_size = sp_header.total_len();
|
||||
let packet_size = sp_header.packet_len();
|
||||
if (current_idx + packet_size) <= buf_len {
|
||||
packet_sender
|
||||
.send_packet(sender_id, &buf[current_idx..current_idx + packet_size])?;
|
||||
@@ -76,7 +76,7 @@ pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
||||
continue;
|
||||
}
|
||||
SpValidity::Skip => {
|
||||
current_idx += sp_header.total_len();
|
||||
current_idx += sp_header.packet_len();
|
||||
}
|
||||
// We might have lost sync. Try to find the start of a new space packet header.
|
||||
SpValidity::Invalid => {
|
||||
@@ -89,9 +89,10 @@ pub fn parse_buffer_for_ccsds_space_packets<SendError>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use arbitrary_int::{u11, u14};
|
||||
use spacepackets::{
|
||||
CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType, SequenceFlags, SpHeader,
|
||||
ecss::tc::PusTcCreator,
|
||||
CcsdsPacket, PacketId, PacketSequenceControl, PacketType, SequenceFlags, SpHeader,
|
||||
ecss::{CreatorConfig, tc::PusTcCreator},
|
||||
};
|
||||
|
||||
use crate::{ComponentId, encoding::tests::TcCacher};
|
||||
@@ -99,8 +100,8 @@ mod tests {
|
||||
use super::{SpValidity, SpacePacketValidator, parse_buffer_for_ccsds_space_packets};
|
||||
|
||||
const PARSER_ID: ComponentId = 0x05;
|
||||
const TEST_APID_0: u16 = 0x02;
|
||||
const TEST_APID_1: u16 = 0x10;
|
||||
const TEST_APID_0: u11 = u11::new(0x02);
|
||||
const TEST_APID_1: u11 = u11::new(0x10);
|
||||
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
|
||||
const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
|
||||
|
||||
@@ -131,7 +132,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
|
||||
let mut buffer: [u8; 32] = [0; 32];
|
||||
let packet_len = ping_tc
|
||||
.write_to_bytes(&mut buffer)
|
||||
@@ -156,8 +157,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_multi_packet() {
|
||||
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
||||
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
|
||||
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], CreatorConfig::default());
|
||||
let mut buffer: [u8; 32] = [0; 32];
|
||||
let packet_len_ping = ping_tc
|
||||
.write_to_bytes(&mut buffer)
|
||||
@@ -191,9 +192,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_multi_apid() {
|
||||
let sph = SpHeader::new_from_apid(TEST_APID_0);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
|
||||
let sph = SpHeader::new_from_apid(TEST_APID_1);
|
||||
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true);
|
||||
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], CreatorConfig::default());
|
||||
let mut buffer: [u8; 32] = [0; 32];
|
||||
let packet_len_ping = ping_tc
|
||||
.write_to_bytes(&mut buffer)
|
||||
@@ -221,10 +222,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_split_packet_multi() {
|
||||
let ping_tc =
|
||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
|
||||
let action_tc =
|
||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true);
|
||||
let ping_tc = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(TEST_APID_0),
|
||||
17,
|
||||
1,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let action_tc = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(TEST_APID_1),
|
||||
8,
|
||||
0,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let mut buffer: [u8; 32] = [0; 32];
|
||||
let packet_len_ping = ping_tc
|
||||
.write_to_bytes(&mut buffer)
|
||||
@@ -255,8 +266,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_one_split_packet() {
|
||||
let ping_tc =
|
||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
|
||||
let ping_tc = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(TEST_APID_0),
|
||||
17,
|
||||
1,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let mut buffer: [u8; 32] = [0; 32];
|
||||
let packet_len_ping = ping_tc
|
||||
.write_to_bytes(&mut buffer)
|
||||
@@ -281,7 +297,7 @@ mod tests {
|
||||
fn test_smallest_packet() {
|
||||
let ccsds_header_only = SpHeader::new(
|
||||
PacketId::new(PacketType::Tc, true, TEST_APID_0),
|
||||
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 0),
|
||||
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(0)),
|
||||
0,
|
||||
);
|
||||
let mut buf: [u8; 7] = [0; 7];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
850
satrs/src/event_man_legacy.rs
Normal file
850
satrs/src/event_man_legacy.rs
Normal file
@@ -0,0 +1,850 @@
|
||||
//! # 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
//! Event support module
|
||||
//! # Event support module
|
||||
//!
|
||||
//! 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 user can define events as custom structs or enumerations.
|
||||
//! The event structures defined here are type erased and rely on some properties which
|
||||
//! should be provided by the user through the [Event] and [serde::Serialize] trait.
|
||||
//!
|
||||
//! This in turn allows to use higher-level abstractions like the event manger.
|
||||
//!
|
||||
//! This module includes the basic type erased event structs [EventErasedAlloc] and
|
||||
//! [EventErasedHeapless].
|
||||
//! 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:
|
||||
//!
|
||||
@@ -10,42 +15,26 @@
|
||||
//! - 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::{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 arbitrary_int::{prelude::*, u14};
|
||||
#[cfg(feature = "heapless")]
|
||||
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;
|
||||
pub const MAX_GROUP_ID_U32_EVENT: u16 = u14::MAX.value();
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
#[derive(
|
||||
Copy, Clone, PartialEq, Eq, Debug, Hash, num_enum::TryFromPrimitive, num_enum::IntoPrimitive,
|
||||
)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
pub enum Severity {
|
||||
Info = 0,
|
||||
Low = 1,
|
||||
@@ -57,801 +46,203 @@ 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;
|
||||
pub trait Event: Clone {
|
||||
fn id(&self) -> EventId;
|
||||
}
|
||||
|
||||
/// Type level support struct
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub struct SeverityLow {}
|
||||
impl HasSeverity for SeverityLow {
|
||||
const SEVERITY: Severity = Severity::Low;
|
||||
}
|
||||
pub type GroupId = u14;
|
||||
|
||||
/// 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)]
|
||||
/// Unique event identifier.
|
||||
///
|
||||
/// Consists of a group ID, a unique ID and the severity.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
struct EventBase<Raw, GroupId, UniqueId> {
|
||||
severity: Severity,
|
||||
pub struct EventId {
|
||||
group_id: GroupId,
|
||||
unique_id: UniqueId,
|
||||
phantom: PhantomData<Raw>,
|
||||
unique_id: u16,
|
||||
severity: Severity,
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
impl EventId {
|
||||
pub fn new(severity: Severity, group_id: u14, unique_id: u16) -> Self {
|
||||
Self {
|
||||
severity,
|
||||
group_id,
|
||||
unique_id,
|
||||
}
|
||||
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
|
||||
pub fn severity(&self) -> Severity {
|
||||
self.severity
|
||||
}
|
||||
}
|
||||
|
||||
impl<RAW, UID> EventBase<RAW, u16, UID> {
|
||||
#[inline]
|
||||
pub fn group_id(&self) -> u16 {
|
||||
pub fn group_id(&self) -> u14 {
|
||||
self.group_id
|
||||
}
|
||||
}
|
||||
|
||||
impl<RAW, UID> EventBase<RAW, u8, UID> {
|
||||
#[inline]
|
||||
pub fn group_id(&self) -> u8 {
|
||||
self.group_id
|
||||
pub fn raw(&self) -> u32 {
|
||||
((self.severity as u32) << 30)
|
||||
| ((self.group_id.as_u16() as u32) << 16)
|
||||
| (self.unique_id as u32)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
impl From<u32> for EventId {
|
||||
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 group_id = u14::new(((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>()
|
||||
/// Event which was type erased and serialized into a [alloc::vec::Vec].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub struct EventErasedAlloc {
|
||||
id: EventId,
|
||||
event_raw: alloc::vec::Vec<u8>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl EventErasedAlloc {
|
||||
#[cfg(feature = "serde")]
|
||||
/// Creates a new event by serializing the given event using [postcard].
|
||||
pub fn new(event: &(impl serde::Serialize + Event)) -> Self {
|
||||
Self {
|
||||
id: event.id(),
|
||||
event_raw: postcard::to_allocvec(event).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
self.base.write_to_bytes(self.raw(), buf, self.size())
|
||||
pub fn new_with_raw_event(id: EventId, event_raw: &[u8]) -> Self {
|
||||
Self {
|
||||
id,
|
||||
event_raw: event_raw.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&self) -> u64 {
|
||||
self.raw().into()
|
||||
#[inline]
|
||||
pub fn raw(&self) -> &[u8] {
|
||||
&self.event_raw
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssEnumeration for EventU32 {
|
||||
fn pfc(&self) -> u8 {
|
||||
u32::BITS as u8
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<T: serde::Serialize + Event> From<T> for EventErasedAlloc {
|
||||
fn from(event: T) -> Self {
|
||||
Self::new(&event)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
#[cfg(feature = "alloc")]
|
||||
impl Event for EventErasedAlloc {
|
||||
fn id(&self) -> EventId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
/// Event which was type erased and serialized into a [heapless::vec::Vec].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg(feature = "heapless")]
|
||||
pub struct EventErasedHeapless<const N: usize> {
|
||||
id: EventId,
|
||||
event_raw: heapless::vec::Vec<u8, N>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "heapless")]
|
||||
impl<const N: usize> Event for EventErasedHeapless<N> {
|
||||
fn id(&self) -> EventId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "heapless")]
|
||||
impl<const N: usize> EventErasedHeapless<N> {
|
||||
#[cfg(feature = "serde")]
|
||||
/// Creates a new event by serializing the given event using [postcard].
|
||||
pub fn new(event: &(impl serde::Serialize + Event)) -> Result<Self, ByteConversionError> {
|
||||
let ser_size = postcard::experimental::serialized_size(event).unwrap();
|
||||
if ser_size > N {
|
||||
return Err(ByteConversionError::ToSliceTooSmall {
|
||||
found: N,
|
||||
expected: ser_size,
|
||||
});
|
||||
}
|
||||
let mut vec = heapless::Vec::<u8, N>::new();
|
||||
vec.resize(N, 0).unwrap();
|
||||
postcard::to_slice(event, vec.as_mut_slice()).unwrap();
|
||||
Ok(Self {
|
||||
id: event.id(),
|
||||
event_raw: vec,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
pub fn new_with_raw_event(id: EventId, event_raw: heapless::Vec<u8, N>) -> Self {
|
||||
Self { id, event_raw }
|
||||
}
|
||||
|
||||
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()
|
||||
pub fn raw(&self) -> &[u8] {
|
||||
&self.event_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);
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum TestEvent {
|
||||
Info,
|
||||
ErrorOtherGroup,
|
||||
}
|
||||
|
||||
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)
|
||||
impl Event for TestEvent {
|
||||
fn id(&self) -> EventId {
|
||||
match self {
|
||||
TestEvent::Info => EventId::new(Severity::Info, u14::new(0), 0),
|
||||
TestEvent::ErrorOtherGroup => EventId::new(Severity::High, u14::new(1), 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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!(TestEvent::Info.id().severity(), Severity::Info);
|
||||
assert_eq!(TestEvent::Info.id().unique_id(), 0);
|
||||
assert_eq!(TestEvent::Info.id().group_id().value(), 0);
|
||||
assert_eq!(TestEvent::ErrorOtherGroup.id().group_id().value(), 1);
|
||||
assert_eq!(TestEvent::ErrorOtherGroup.id().unique_id(), 1);
|
||||
let raw_event = TestEvent::Info.id().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);
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_basic_erased_alloc_event() {
|
||||
let event = EventErasedAlloc::new(&TestEvent::Info);
|
||||
let test_event: TestEvent = postcard::from_bytes(event.raw()).unwrap();
|
||||
assert_eq!(test_event, TestEvent::Info);
|
||||
}
|
||||
|
||||
#[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());
|
||||
#[cfg(all(feature = "serde", feature = "alloc"))]
|
||||
fn test_basic_erased_heapless_event() {
|
||||
let event = EventErasedHeapless::<8>::new(&TestEvent::Info).unwrap();
|
||||
let test_event: TestEvent = postcard::from_bytes(event.raw()).unwrap();
|
||||
assert_eq!(test_event, TestEvent::Info);
|
||||
}
|
||||
}
|
||||
|
||||
859
satrs/src/events_legacy.rs
Normal file
859
satrs/src/events_legacy.rs
Normal file
@@ -0,0 +1,859 @@
|
||||
//! # 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());
|
||||
}
|
||||
}
|
||||
@@ -183,10 +183,11 @@ mod tests {
|
||||
};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use arbitrary_int::u11;
|
||||
use hashbrown::HashSet;
|
||||
use spacepackets::{
|
||||
CcsdsPacket, PacketId, SpHeader,
|
||||
ecss::{WritablePusPacket, tc::PusTcCreator},
|
||||
ecss::{CreatorConfig, WritablePusPacket, tc::PusTcCreator},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -203,9 +204,9 @@ mod tests {
|
||||
use super::TcpSpacepacketsServer;
|
||||
|
||||
const TCP_SERVER_ID: ComponentId = 0x05;
|
||||
const TEST_APID_0: u16 = 0x02;
|
||||
const TEST_APID_0: u11 = u11::new(0x02);
|
||||
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
|
||||
const TEST_APID_1: u16 = 0x10;
|
||||
const TEST_APID_1: u11 = u11::new(0x10);
|
||||
const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -283,8 +284,13 @@ mod tests {
|
||||
.check_no_connections_left();
|
||||
set_if_done.store(true, Ordering::Relaxed);
|
||||
});
|
||||
let ping_tc =
|
||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
|
||||
let ping_tc = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(TEST_APID_0),
|
||||
17,
|
||||
1,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let tc_0 = ping_tc.to_vec().expect("packet generation failed");
|
||||
let mut stream = TcpStream::connect(dest_addr).expect("connecting to TCP server failed");
|
||||
stream
|
||||
@@ -314,13 +320,23 @@ mod tests {
|
||||
|
||||
// Add telemetry
|
||||
let mut total_tm_len = 0;
|
||||
let verif_tm =
|
||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 1, 1, &[], true);
|
||||
let verif_tm = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(TEST_APID_0),
|
||||
1,
|
||||
1,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let tm_0 = verif_tm.to_vec().expect("writing packet failed");
|
||||
total_tm_len += tm_0.len();
|
||||
tm_source.add_tm(&tm_0);
|
||||
let verif_tm =
|
||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 1, 3, &[], true);
|
||||
let verif_tm = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(TEST_APID_1),
|
||||
1,
|
||||
3,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let tm_1 = verif_tm.to_vec().expect("writing packet failed");
|
||||
total_tm_len += tm_1.len();
|
||||
tm_source.add_tm(&tm_1);
|
||||
@@ -366,14 +382,24 @@ mod tests {
|
||||
.expect("setting reas timeout failed");
|
||||
|
||||
// Send telecommands
|
||||
let ping_tc =
|
||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
|
||||
let ping_tc = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(TEST_APID_0),
|
||||
17,
|
||||
1,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let tc_0 = ping_tc.to_vec().expect("ping tc creation failed");
|
||||
stream
|
||||
.write_all(&tc_0)
|
||||
.expect("writing to TCP server failed");
|
||||
let action_tc =
|
||||
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true);
|
||||
let action_tc = PusTcCreator::new_simple(
|
||||
SpHeader::new_from_apid(TEST_APID_1),
|
||||
8,
|
||||
0,
|
||||
&[],
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let tc_1 = action_tc.to_vec().expect("action tc creation failed");
|
||||
stream
|
||||
.write_all(&tc_1)
|
||||
|
||||
@@ -26,7 +26,8 @@ use std::vec::Vec;
|
||||
/// use satrs::ComponentId;
|
||||
/// use satrs::tmtc::PacketSenderRaw;
|
||||
/// use spacepackets::SpHeader;
|
||||
/// use spacepackets::ecss::tc::PusTcCreator;
|
||||
/// use spacepackets::ecss::tc::{PusTcCreator, CreatorConfig};
|
||||
/// use arbitrary_int::u11;
|
||||
///
|
||||
/// const UDP_SERVER_ID: ComponentId = 0x05;
|
||||
///
|
||||
@@ -34,8 +35,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(0x02);
|
||||
/// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
||||
/// let sph = SpHeader::new_from_apid(u11::new(0x02));
|
||||
/// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
|
||||
/// // Can not fail.
|
||||
/// let ping_tc_raw = pus_tc.to_vec().unwrap();
|
||||
///
|
||||
@@ -127,8 +128,10 @@ mod tests {
|
||||
use crate::hal::std::udp_server::{ReceiveResult, UdpTcServer};
|
||||
use crate::queue::GenericSendError;
|
||||
use crate::tmtc::PacketSenderRaw;
|
||||
use arbitrary_int::u11;
|
||||
use core::cell::RefCell;
|
||||
use spacepackets::SpHeader;
|
||||
use spacepackets::ecss::CreatorConfig;
|
||||
use spacepackets::ecss::tc::PusTcCreator;
|
||||
use std::collections::VecDeque;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
||||
@@ -165,8 +168,8 @@ mod tests {
|
||||
let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, ping_receiver)
|
||||
.expect("Creating UDP TMTC server failed");
|
||||
is_send(&udp_tc_server);
|
||||
let sph = SpHeader::new_from_apid(0x02);
|
||||
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
||||
let sph = SpHeader::new_from_apid(u11::new(0x02));
|
||||
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
|
||||
let len = pus_tc
|
||||
.write_to_bytes(&mut buf)
|
||||
.expect("Error writing PUS TC packet");
|
||||
|
||||
@@ -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(feature = "alloc")]
|
||||
#[cfg(any(feature = "alloc", test))]
|
||||
extern crate alloc;
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate downcast_rs;
|
||||
@@ -27,7 +27,9 @@ 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;
|
||||
@@ -49,6 +51,7 @@ pub mod scheduling;
|
||||
pub mod subsystem;
|
||||
pub mod time;
|
||||
pub mod tmtc;
|
||||
pub mod ccsds;
|
||||
|
||||
pub use spacepackets;
|
||||
|
||||
|
||||
@@ -258,6 +258,9 @@ 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.
|
||||
@@ -714,6 +717,13 @@ 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)?;
|
||||
@@ -1055,6 +1065,13 @@ 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 {
|
||||
@@ -1597,223 +1614,228 @@ 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
|
||||
);
|
||||
|
||||
fn small_heapless_pool() -> StaticHeaplessMemoryPool<3> {
|
||||
let mut heapless_pool: StaticHeaplessMemoryPool<3> =
|
||||
StaticHeaplessMemoryPool::new(false);
|
||||
assert!(
|
||||
heapless_pool
|
||||
.grow(
|
||||
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
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_3.take(),
|
||||
SUBPOOL_3_SIZES.take(),
|
||||
SUBPOOL_3_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
heapless_pool
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
let mut pool_provider = make_heapless_pool!(T2);
|
||||
generic_test_delete(&mut pool_provider);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_modify() {
|
||||
let mut pool_provider = small_heapless_pool();
|
||||
let mut pool_provider = make_heapless_pool!(T3);
|
||||
generic_test_modify(&mut pool_provider);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_consecutive_reservation() {
|
||||
let mut pool_provider = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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 = small_heapless_pool();
|
||||
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);
|
||||
assert!(
|
||||
heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_2.take(),
|
||||
SUBPOOL_2_SIZES.take(),
|
||||
SUBPOOL_2_T18.take(),
|
||||
SUBPOOL_2_SIZES_T18.take(),
|
||||
SUBPOOL_2_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@@ -1822,8 +1844,8 @@ mod tests {
|
||||
assert!(
|
||||
heapless_pool
|
||||
.grow(
|
||||
SUBPOOL_4.take(),
|
||||
SUBPOOL_4_SIZES.take(),
|
||||
SUBPOOL_4_T18.take(),
|
||||
SUBPOOL_4_SIZES_T18.take(),
|
||||
SUBPOOL_4_NUM_ELEMENTS,
|
||||
true
|
||||
)
|
||||
@@ -1836,6 +1858,18 @@ 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(
|
||||
@@ -1863,6 +1897,24 @@ 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(
|
||||
@@ -1898,6 +1950,24 @@ 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!(
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
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::{MAX_APID, SpHeader};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
@@ -11,16 +13,13 @@ pub use alloc_mod::*;
|
||||
pub use spacepackets::ecss::event::*;
|
||||
|
||||
pub struct EventReportCreator {
|
||||
apid: u16,
|
||||
apid: u11,
|
||||
pub dest_id: u16,
|
||||
}
|
||||
|
||||
impl EventReportCreator {
|
||||
pub fn new(apid: u16, dest_id: u16) -> Option<Self> {
|
||||
if apid > MAX_APID {
|
||||
return None;
|
||||
}
|
||||
Some(Self { dest_id, apid })
|
||||
pub fn new(apid: u11, dest_id: u16) -> Self {
|
||||
Self { dest_id, apid }
|
||||
}
|
||||
|
||||
pub fn event_info<'time, 'src_data>(
|
||||
@@ -124,7 +123,7 @@ impl EventReportCreator {
|
||||
SpHeader::new_from_apid(self.apid),
|
||||
sec_header,
|
||||
&src_data_buf[0..current_idx],
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -162,34 +161,34 @@ mod alloc_mod {
|
||||
impl EventReporter<DummyEventHook> {
|
||||
pub fn new(
|
||||
id: ComponentId,
|
||||
default_apid: u16,
|
||||
default_apid: u11,
|
||||
default_dest_id: u16,
|
||||
max_event_id_and_aux_data_size: usize,
|
||||
) -> Option<Self> {
|
||||
let reporter = EventReportCreator::new(default_apid, default_dest_id)?;
|
||||
Some(Self {
|
||||
) -> Self {
|
||||
let reporter = EventReportCreator::new(default_apid, default_dest_id);
|
||||
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> {
|
||||
pub fn new_with_hook(
|
||||
id: ComponentId,
|
||||
default_apid: u16,
|
||||
default_apid: u11,
|
||||
default_dest_id: u16,
|
||||
max_event_id_and_aux_data_size: usize,
|
||||
tm_hook: EventTmHookInstance,
|
||||
) -> Option<Self> {
|
||||
let reporter = EventReportCreator::new(default_apid, default_dest_id)?;
|
||||
Some(Self {
|
||||
) -> Self {
|
||||
let reporter = EventReportCreator::new(default_apid, default_dest_id);
|
||||
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(
|
||||
@@ -266,7 +265,7 @@ mod alloc_mod {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ComponentId;
|
||||
use crate::events::{EventU32, Severity};
|
||||
use crate::events_legacy::{EventU32, Severity};
|
||||
use crate::pus::test_util::TEST_COMPONENT_ID_0;
|
||||
use crate::pus::tests::CommonTmInfo;
|
||||
use crate::pus::{ChannelWithId, EcssTmSender, EcssTmtcError, PusTmVariant};
|
||||
@@ -276,7 +275,7 @@ mod tests {
|
||||
use std::collections::VecDeque;
|
||||
use std::vec::Vec;
|
||||
|
||||
const EXAMPLE_APID: u16 = 0xee;
|
||||
const EXAMPLE_APID: u11 = u11::new(0xee);
|
||||
const EXAMPLE_GROUP_ID: u16 = 2;
|
||||
const EXAMPLE_EVENT_ID_0: u16 = 1;
|
||||
#[allow(dead_code)]
|
||||
@@ -376,14 +375,12 @@ mod tests {
|
||||
error_data: Option<&[u8]>,
|
||||
) {
|
||||
let mut sender = TestSender::default();
|
||||
let reporter = EventReporter::new(
|
||||
let mut 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 {
|
||||
@@ -474,9 +471,7 @@ mod tests {
|
||||
fn insufficient_buffer() {
|
||||
let mut sender = TestSender::default();
|
||||
for i in 0..3 {
|
||||
let reporter = EventReporter::new(0, EXAMPLE_APID, 0, i);
|
||||
assert!(reporter.is_some());
|
||||
let mut reporter = reporter.unwrap();
|
||||
let mut reporter = EventReporter::new(0, EXAMPLE_APID, 0, i);
|
||||
check_buf_too_small(&mut reporter, &mut sender, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::events::{EventU32, GenericEvent, Severity};
|
||||
use crate::events_legacy::{EventU32, GenericEvent, Severity};
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::events::{EventU32TypedSev, HasSeverity};
|
||||
use crate::events_legacy::{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::EventU16,
|
||||
events_legacy::EventU16,
|
||||
params::{Params, WritableToBeBytes},
|
||||
pus::event::{DummyEventHook, EventTmHook},
|
||||
};
|
||||
@@ -311,29 +311,28 @@ pub mod alloc_mod {
|
||||
mod tests {
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec;
|
||||
use arbitrary_int::u11;
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use spacepackets::ecss::event::Subservice;
|
||||
use spacepackets::ecss::tm::PusTmReader;
|
||||
|
||||
use super::*;
|
||||
use crate::request::UniqueApidTargetId;
|
||||
use crate::{events::SeverityInfo, tmtc::PacketAsVec};
|
||||
use crate::{events_legacy::SeverityInfo, tmtc::PacketAsVec};
|
||||
use std::sync::mpsc::{self, TryRecvError};
|
||||
|
||||
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(1, 0);
|
||||
const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5);
|
||||
const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||
const TEST_APID: u16 = 0x02;
|
||||
const TEST_APID: u11 = u11::new(0x02);
|
||||
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)
|
||||
.expect("Creating event repoter failed");
|
||||
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128);
|
||||
PusEventTmCreatorWithMap::new_with_default_backend(reporter)
|
||||
}
|
||||
fn create_basic_man_2() -> DefaultPusEventU32TmCreator {
|
||||
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
|
||||
.expect("Creating event repoter failed");
|
||||
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128);
|
||||
let backend = DefaultPusEventReportingMap::default();
|
||||
PusEventTmCreatorWithMap::new(reporter, backend)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::events::EventU32;
|
||||
use crate::events_legacy::EventU32;
|
||||
use crate::pus::event_man::{EventRequest, EventRequestWithToken};
|
||||
use crate::pus::verification::TcStateToken;
|
||||
use crate::pus::{DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError};
|
||||
@@ -144,7 +144,10 @@ impl<
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
use arbitrary_int::u14;
|
||||
use delegate::delegate;
|
||||
use spacepackets::ecss::CreatorConfig;
|
||||
use spacepackets::ecss::event::Subservice;
|
||||
use spacepackets::time::{TimeWriter, cds};
|
||||
use spacepackets::util::UnsignedEnum;
|
||||
@@ -165,7 +168,7 @@ mod tests {
|
||||
use crate::pus::{GenericConversionError, HandlingStatus, MpscTcReceiver};
|
||||
use crate::tmtc::PacketSenderWithSharedPool;
|
||||
use crate::{
|
||||
events::EventU32,
|
||||
events_legacy::EventU32,
|
||||
pus::{
|
||||
DirectPusPacketHandlerResult, EcssTcInSharedPoolCacher, PusPacketHandlingError,
|
||||
event_man::EventRequestWithToken,
|
||||
@@ -176,7 +179,7 @@ mod tests {
|
||||
|
||||
use super::PusEventServiceHandler;
|
||||
|
||||
const TEST_EVENT_0: EventU32 = EventU32::new(crate::events::Severity::Info, 5, 25);
|
||||
const TEST_EVENT_0: EventU32 = EventU32::new(crate::events_legacy::Severity::Info, 5, 25);
|
||||
|
||||
struct Pus5HandlerWithStoreTester {
|
||||
common: PusServiceHandlerWithSharedStoreCommon,
|
||||
@@ -242,13 +245,13 @@ mod tests {
|
||||
expected_event_req: EventRequest,
|
||||
event_req_receiver: mpsc::Receiver<EventRequestWithToken>,
|
||||
) {
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(5, subservice as u8);
|
||||
let mut app_data = [0; 4];
|
||||
TEST_EVENT_0
|
||||
.write_to_be_bytes(&mut app_data)
|
||||
.expect("writing test event failed");
|
||||
let ping_tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
|
||||
let ping_tc = PusTcCreator::new(sp_header, sec_header, &app_data, CreatorConfig::default());
|
||||
let token = test_harness.start_verification(&ping_tc);
|
||||
test_harness.send_tc(&token, &ping_tc);
|
||||
let request_id = token.request_id();
|
||||
@@ -307,9 +310,10 @@ mod tests {
|
||||
fn test_sending_custom_subservice() {
|
||||
let (event_request_tx, _) = mpsc::channel();
|
||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(5, 200);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
||||
let ping_tc =
|
||||
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||
let token = test_harness.start_verification(&ping_tc);
|
||||
test_harness.send_tc(&token, &ping_tc);
|
||||
let result = test_harness.handle_one_tc();
|
||||
@@ -326,10 +330,11 @@ mod tests {
|
||||
fn test_sending_invalid_app_data() {
|
||||
let (event_request_tx, _) = mpsc::channel();
|
||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let sec_header =
|
||||
PusTcSecondaryHeader::new_simple(5, Subservice::TcEnableEventGeneration as u8);
|
||||
let ping_tc = PusTcCreator::new(sp_header, sec_header, &[0, 1, 2], true);
|
||||
let ping_tc =
|
||||
PusTcCreator::new(sp_header, sec_header, &[0, 1, 2], CreatorConfig::default());
|
||||
let token = test_harness.start_verification(&ping_tc);
|
||||
test_harness.send_tc(&token, &ping_tc);
|
||||
let result = test_harness.handle_one_tc();
|
||||
|
||||
@@ -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 [ActiveRequestProvider] trait bound.
|
||||
/// the [ActiveRequest] 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 [EcssTcInMemConverter] abstraction. This object provides some convenience
|
||||
/// by using the [CacheAndReadRawEcssTc] abstraction. This object provides some convenience
|
||||
/// methods to make the generic parts of TC handling easier.
|
||||
pub struct PusServiceHelper<
|
||||
TcReceiver: EcssTcReceiver,
|
||||
@@ -1258,6 +1258,7 @@ pub(crate) fn source_buffer_large_enough(
|
||||
|
||||
#[cfg(any(feature = "test_util", test))]
|
||||
pub mod test_util {
|
||||
use arbitrary_int::u11;
|
||||
use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
|
||||
|
||||
use crate::request::UniqueApidTargetId;
|
||||
@@ -1267,7 +1268,7 @@ pub mod test_util {
|
||||
verification::{self, TcStateAccepted, VerificationToken},
|
||||
};
|
||||
|
||||
pub const TEST_APID: u16 = 0x101;
|
||||
pub const TEST_APID: u11 = u11::new(0x101);
|
||||
pub const TEST_UNIQUE_ID_0: u32 = 0x05;
|
||||
pub const TEST_UNIQUE_ID_1: u32 = 0x06;
|
||||
|
||||
@@ -1302,6 +1303,7 @@ pub mod tests {
|
||||
|
||||
use alloc::collections::VecDeque;
|
||||
use alloc::vec::Vec;
|
||||
use arbitrary_int::{u11, u14};
|
||||
use satrs_shared::res_code::ResultU16;
|
||||
use spacepackets::CcsdsPacket;
|
||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
|
||||
@@ -1324,8 +1326,8 @@ pub mod tests {
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub(crate) struct CommonTmInfo {
|
||||
pub subservice: u8,
|
||||
pub apid: u16,
|
||||
pub seq_count: u16,
|
||||
pub apid: u11,
|
||||
pub seq_count: u14,
|
||||
pub msg_counter: u16,
|
||||
pub dest_id: u16,
|
||||
pub timestamp: Vec<u8>,
|
||||
@@ -1334,8 +1336,8 @@ pub mod tests {
|
||||
impl CommonTmInfo {
|
||||
pub fn new(
|
||||
subservice: u8,
|
||||
apid: u16,
|
||||
seq_count: u16,
|
||||
apid: u11,
|
||||
seq_count: u14,
|
||||
msg_counter: u16,
|
||||
dest_id: u16,
|
||||
timestamp: &[u8],
|
||||
@@ -1351,11 +1353,11 @@ pub mod tests {
|
||||
}
|
||||
pub fn new_zero_seq_count(
|
||||
subservice: u8,
|
||||
apid: u16,
|
||||
apid: u11,
|
||||
dest_id: u16,
|
||||
timestamp: &[u8],
|
||||
) -> Self {
|
||||
Self::new(subservice, apid, 0, 0, dest_id, timestamp)
|
||||
Self::new(subservice, apid, u14::new(0), 0, dest_id, timestamp)
|
||||
}
|
||||
|
||||
pub fn new_from_tm(tm: &PusTmCreator) -> Self {
|
||||
@@ -1407,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).unwrap();
|
||||
let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||
let verification_handler =
|
||||
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
|
||||
let test_srv_tm_sender =
|
||||
@@ -1500,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).unwrap();
|
||||
let verif_cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||
let verification_handler =
|
||||
VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
|
||||
let in_store_converter = EcssTcVecCacher::default();
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
//!
|
||||
//! 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 core::fmt::{Debug, Display, Formatter};
|
||||
use arbitrary_int::{u11, u14};
|
||||
use core::fmt::Debug;
|
||||
use core::time::Duration;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -11,8 +12,6 @@ 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")]
|
||||
@@ -24,22 +23,26 @@ 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: u16,
|
||||
pub(crate) seq_count: u16,
|
||||
pub(crate) apid: u11,
|
||||
pub(crate) seq_count: u14,
|
||||
}
|
||||
|
||||
impl RequestId {
|
||||
pub fn source_id(&self) -> u16 {
|
||||
#[inline]
|
||||
pub const fn source_id(&self) -> u16 {
|
||||
self.source_id
|
||||
}
|
||||
|
||||
pub fn apid(&self) -> u16 {
|
||||
#[inline]
|
||||
pub const fn apid(&self) -> u11 {
|
||||
self.apid
|
||||
}
|
||||
|
||||
pub fn seq_count(&self) -> u16 {
|
||||
#[inline]
|
||||
pub const fn seq_count(&self) -> u14 {
|
||||
self.seq_count
|
||||
}
|
||||
|
||||
@@ -53,8 +56,10 @@ impl RequestId {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_u64(&self) -> u64 {
|
||||
((self.source_id as u64) << 32) | ((self.apid as u64) << 16) | self.seq_count as u64
|
||||
pub const fn as_u64(&self) -> u64 {
|
||||
((self.source_id as u64) << 32)
|
||||
| ((self.apid.value() as u64) << 16)
|
||||
| self.seq_count.value() as u64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,107 +142,39 @@ impl<TimeProvider: CcsdsTimeProvider + Clone> TimeWindow<TimeProvider> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ScheduleError {
|
||||
PusError(PusError),
|
||||
#[error("pus error: {0}")]
|
||||
PusError(#[from] 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,
|
||||
StoreError(PoolError),
|
||||
#[error("store error")]
|
||||
Pool(#[from] PoolError),
|
||||
#[error("tc data empty")]
|
||||
TcDataEmpty,
|
||||
TimestampError(TimestampError),
|
||||
#[error("timestamp error: {0}")]
|
||||
TimestampError(#[from] TimestampError),
|
||||
#[error("wrong subservice number {0}")]
|
||||
WrongSubservice(u8),
|
||||
#[error("wrong service number {0}")]
|
||||
WrongService(u8),
|
||||
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,
|
||||
}
|
||||
}
|
||||
#[error("byte conversion error: {0}")]
|
||||
ByteConversionError(#[from] ByteConversionError),
|
||||
}
|
||||
|
||||
/// Generic trait for scheduler objects which are able to schedule ECSS PUS C packets.
|
||||
pub trait PusSchedulerProvider {
|
||||
pub trait PusScheduler {
|
||||
type TimeProvider: CcsdsTimeProvider + TimeReader;
|
||||
|
||||
fn reset(&mut self, store: &mut (impl PoolProvider + ?Sized)) -> Result<(), PoolError>;
|
||||
@@ -394,7 +331,7 @@ pub mod alloc_mod {
|
||||
///
|
||||
/// Currently, sub-schedules and groups are not supported.
|
||||
#[derive(Debug)]
|
||||
pub struct PusScheduler {
|
||||
pub struct PusSchedulerAlloc {
|
||||
// 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
|
||||
@@ -404,7 +341,8 @@ pub mod alloc_mod {
|
||||
time_margin: Duration,
|
||||
enabled: bool,
|
||||
}
|
||||
impl PusScheduler {
|
||||
|
||||
impl PusSchedulerAlloc {
|
||||
/// Create a new PUS scheduler.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -416,7 +354,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 {
|
||||
PusScheduler {
|
||||
PusSchedulerAlloc {
|
||||
tc_map: Default::default(),
|
||||
current_time: init_current_time,
|
||||
time_margin,
|
||||
@@ -438,10 +376,12 @@ 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
|
||||
}
|
||||
@@ -785,7 +725,7 @@ pub mod alloc_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl PusSchedulerProvider for PusScheduler {
|
||||
impl PusScheduler for PusSchedulerAlloc {
|
||||
type TimeProvider = cds::CdsTime;
|
||||
|
||||
/// This will disable the scheduler and clear the schedule as specified in 6.11.4.4.
|
||||
@@ -855,10 +795,12 @@ mod tests {
|
||||
PoolAddr, PoolError, PoolProvider, StaticMemoryPool, StaticPoolAddr, StaticPoolConfig,
|
||||
};
|
||||
use alloc::collections::btree_map::Range;
|
||||
use spacepackets::ecss::WritablePusPacket;
|
||||
use arbitrary_int::traits::Integer;
|
||||
use arbitrary_int::{u11, u14};
|
||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
||||
use spacepackets::ecss::{CreatorConfig, WritablePusPacket};
|
||||
use spacepackets::time::{TimeWriter, UnixTime, cds};
|
||||
use spacepackets::{PacketId, PacketSequenceCtrl, PacketType, SequenceFlags, SpHeader};
|
||||
use spacepackets::{PacketId, PacketSequenceControl, PacketType, SequenceFlags, SpHeader};
|
||||
use std::time::Duration;
|
||||
use std::vec::Vec;
|
||||
#[allow(unused_imports)]
|
||||
@@ -869,28 +811,28 @@ mod tests {
|
||||
cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent)
|
||||
.unwrap();
|
||||
let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
|
||||
let len_packet = base_ping_tc_simple_ctor(0, &[])
|
||||
let len_packet = base_ping_tc_simple_ctor(u14::new(0), &[])
|
||||
.write_to_bytes(&mut buf[len_time_stamp..])
|
||||
.unwrap();
|
||||
(
|
||||
SpHeader::new_for_unseg_tc(0x02, 0x34, len_packet as u16),
|
||||
SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), len_packet as u16),
|
||||
len_packet + len_time_stamp,
|
||||
)
|
||||
}
|
||||
|
||||
fn scheduled_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
|
||||
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
||||
PusTcCreator::new_simple(sph, 11, 4, &buf[..len_app_data], true)
|
||||
PusTcCreator::new_simple(sph, 11, 4, &buf[..len_app_data], CreatorConfig::default())
|
||||
}
|
||||
|
||||
fn wrong_tc_service(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
|
||||
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
||||
PusTcCreator::new_simple(sph, 12, 4, &buf[..len_app_data], true)
|
||||
PusTcCreator::new_simple(sph, 12, 4, &buf[..len_app_data], CreatorConfig::default())
|
||||
}
|
||||
|
||||
fn wrong_tc_subservice(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
|
||||
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
|
||||
PusTcCreator::new_simple(sph, 11, 5, &buf[..len_app_data], true)
|
||||
PusTcCreator::new_simple(sph, 11, 5, &buf[..len_app_data], CreatorConfig::default())
|
||||
}
|
||||
|
||||
fn double_wrapped_time_tagged_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator<'_> {
|
||||
@@ -898,30 +840,37 @@ mod tests {
|
||||
cds::CdsTime::from_unix_time_with_u16_days(×tamp, cds::SubmillisPrecision::Absent)
|
||||
.unwrap();
|
||||
let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
|
||||
let sph = SpHeader::new_for_unseg_tc(0x02, 0x34, 0);
|
||||
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 0);
|
||||
// app data should not matter, double wrapped time-tagged commands should be rejected right
|
||||
// away
|
||||
let inner_time_tagged_tc = PusTcCreator::new_simple(sph, 11, 4, &[], true);
|
||||
let inner_time_tagged_tc =
|
||||
PusTcCreator::new_simple(sph, 11, 4, &[], CreatorConfig::default());
|
||||
let packet_len = inner_time_tagged_tc
|
||||
.write_to_bytes(&mut buf[len_time_stamp..])
|
||||
.expect("writing inner time tagged tc failed");
|
||||
PusTcCreator::new_simple(sph, 11, 4, &buf[..len_time_stamp + packet_len], true)
|
||||
PusTcCreator::new_simple(
|
||||
sph,
|
||||
11,
|
||||
4,
|
||||
&buf[..len_time_stamp + packet_len],
|
||||
CreatorConfig::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn invalid_time_tagged_cmd() -> PusTcCreator<'static> {
|
||||
let sph = SpHeader::new_for_unseg_tc(0x02, 0x34, 1);
|
||||
PusTcCreator::new_simple(sph, 11, 4, &[], true)
|
||||
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), u14::new(0x34), 1);
|
||||
PusTcCreator::new_simple(sph, 11, 4, &[], CreatorConfig::default())
|
||||
}
|
||||
|
||||
fn base_ping_tc_simple_ctor(seq_count: u16, app_data: &'static [u8]) -> PusTcCreator<'static> {
|
||||
let sph = SpHeader::new_for_unseg_tc(0x02, seq_count, 0);
|
||||
PusTcCreator::new_simple(sph, 17, 1, app_data, true)
|
||||
fn base_ping_tc_simple_ctor(seq_count: u14, app_data: &'static [u8]) -> PusTcCreator<'static> {
|
||||
let sph = SpHeader::new_for_unseg_tc(u11::new(0x02), seq_count, 0);
|
||||
PusTcCreator::new_simple(sph, 17, 1, app_data, CreatorConfig::default())
|
||||
}
|
||||
|
||||
fn ping_tc_to_store(
|
||||
pool: &mut StaticMemoryPool,
|
||||
buf: &mut [u8],
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
app_data: &'static [u8],
|
||||
) -> TcInfo {
|
||||
let ping_tc = base_ping_tc_simple_ctor(seq_count, app_data);
|
||||
@@ -932,7 +881,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_enable_api() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
assert!(scheduler.is_enabled());
|
||||
scheduler.disable();
|
||||
assert!(!scheduler.is_enabled());
|
||||
@@ -946,10 +896,11 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::new(0), &[]);
|
||||
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(
|
||||
@@ -959,7 +910,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let app_data = &[0, 1, 2];
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, app_data);
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), app_data);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(
|
||||
UnixTime::new_only_secs(200),
|
||||
@@ -968,7 +919,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let app_data = &[0, 1, 2];
|
||||
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, app_data);
|
||||
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, u14::new(2), app_data);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(
|
||||
UnixTime::new_only_secs(300),
|
||||
@@ -988,7 +939,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn insert_multi_with_same_time() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(
|
||||
@@ -999,8 +951,8 @@ mod tests {
|
||||
packet_idx: 1,
|
||||
}),
|
||||
RequestId {
|
||||
seq_count: 1,
|
||||
apid: 0,
|
||||
seq_count: u14::new(1),
|
||||
apid: u11::ZERO,
|
||||
source_id: 0,
|
||||
},
|
||||
),
|
||||
@@ -1016,8 +968,8 @@ mod tests {
|
||||
packet_idx: 2,
|
||||
}),
|
||||
RequestId {
|
||||
seq_count: 2,
|
||||
apid: 1,
|
||||
seq_count: u14::new(2),
|
||||
apid: u11::new(1),
|
||||
source_id: 5,
|
||||
},
|
||||
),
|
||||
@@ -1035,8 +987,8 @@ mod tests {
|
||||
.into(),
|
||||
RequestId {
|
||||
source_id: 10,
|
||||
seq_count: 20,
|
||||
apid: 23,
|
||||
seq_count: u14::new(20),
|
||||
apid: u11::new(23),
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -1047,7 +999,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_time_update() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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);
|
||||
@@ -1077,19 +1030,22 @@ mod tests {
|
||||
#[test]
|
||||
fn test_request_id() {
|
||||
let src_id_to_set = 12;
|
||||
let apid_to_set = 0x22;
|
||||
let seq_count = 105;
|
||||
let sp_header = SpHeader::new_for_unseg_tc(apid_to_set, 105, 0);
|
||||
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 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, true);
|
||||
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.as_u64(),
|
||||
((src_id_to_set as u64) << 32) | (apid_to_set as u64) << 16 | seq_count as u64
|
||||
((src_id_to_set as u64) << 32)
|
||||
| (apid_to_set.value() as u64) << 16
|
||||
| seq_count.value() as u64
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
@@ -1098,16 +1054,17 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("insertion failed");
|
||||
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
||||
.expect("insertion failed");
|
||||
@@ -1166,16 +1123,17 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("insertion failed");
|
||||
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1)
|
||||
.expect("insertion failed");
|
||||
@@ -1226,18 +1184,19 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("insertion failed");
|
||||
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
||||
.expect("insertion failed");
|
||||
@@ -1291,14 +1250,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn insert_unwrapped_tc() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
|
||||
let info = scheduler
|
||||
.insert_unwrapped_tc(
|
||||
@@ -1313,7 +1273,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(0, &[]));
|
||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::ZERO, &[]));
|
||||
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
|
||||
|
||||
@@ -1335,13 +1295,14 @@ mod tests {
|
||||
|
||||
let read_len = pool.read(&addr_vec[0], &mut read_buf).unwrap();
|
||||
let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
|
||||
assert_eq!(read_len, check_tc.total_len());
|
||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(0, &[]));
|
||||
assert_eq!(read_len, check_tc.packet_len());
|
||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::new(0), &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_wrapped_tc() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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)],
|
||||
@@ -1362,8 +1323,8 @@ 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.total_len());
|
||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(0, &[]));
|
||||
assert_eq!(read_len, check_tc.packet_len());
|
||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::ZERO, &[]));
|
||||
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
|
||||
|
||||
@@ -1387,13 +1348,14 @@ mod tests {
|
||||
|
||||
let read_len = pool.read(&addr_vec[0], &mut buf).unwrap();
|
||||
let check_tc = PusTcReader::new(&buf).expect("incorrect PUS tc raw data");
|
||||
assert_eq!(read_len, check_tc.total_len());
|
||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(0, &[]));
|
||||
assert_eq!(read_len, check_tc.packet_len());
|
||||
assert_eq!(check_tc, base_ping_tc_simple_ctor(u14::new(0), &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_wrong_service() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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)],
|
||||
@@ -1418,7 +1380,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn insert_wrong_subservice() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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)],
|
||||
@@ -1443,7 +1406,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn insert_wrapped_tc_faulty_app_data() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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,
|
||||
@@ -1460,7 +1424,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn insert_doubly_wrapped_time_tagged_cmd() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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,
|
||||
@@ -1478,7 +1443,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_ctor_from_current() {
|
||||
let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
|
||||
let scheduler = PusSchedulerAlloc::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);
|
||||
@@ -1486,7 +1451,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_update_from_current() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
assert_eq!(scheduler.current_time.as_secs(), 0);
|
||||
scheduler
|
||||
.update_time_from_now()
|
||||
@@ -1496,7 +1462,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn release_time_within_time_margin() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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)],
|
||||
@@ -1529,9 +1496,10 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("insertion failed");
|
||||
@@ -1566,9 +1534,10 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("insertion failed");
|
||||
@@ -1592,9 +1561,10 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("inserting tc failed");
|
||||
@@ -1613,9 +1583,10 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("inserting tc failed");
|
||||
@@ -1634,17 +1605,18 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("inserting tc failed");
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1)
|
||||
.expect("inserting tc failed");
|
||||
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, &[]);
|
||||
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, u14::new(2), &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_2)
|
||||
.expect("inserting tc failed");
|
||||
@@ -1676,7 +1648,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn insert_full_store_test() {
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
|
||||
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
|
||||
vec![(1, 64)],
|
||||
@@ -1692,7 +1665,7 @@ mod tests {
|
||||
assert!(insert_res.is_err());
|
||||
let err = insert_res.unwrap_err();
|
||||
match err {
|
||||
ScheduleError::StoreError(e) => match e {
|
||||
ScheduleError::Pool(e) => match e {
|
||||
PoolError::StoreFull(_) => {}
|
||||
_ => panic!("unexpected store error {e}"),
|
||||
},
|
||||
@@ -1702,8 +1675,8 @@ mod tests {
|
||||
|
||||
fn insert_command_with_release_time(
|
||||
pool: &mut StaticMemoryPool,
|
||||
scheduler: &mut PusScheduler,
|
||||
seq_count: u16,
|
||||
scheduler: &mut PusSchedulerAlloc,
|
||||
seq_count: u14,
|
||||
release_secs: u64,
|
||||
) -> TcInfo {
|
||||
let mut buf: [u8; 32] = [0; 32];
|
||||
@@ -1721,9 +1694,10 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
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, 0, 50);
|
||||
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
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);
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
||||
let check_range = |range: Range<UnixTime, Vec<TcInfo>>| {
|
||||
let mut tcs_in_range = 0;
|
||||
@@ -1753,10 +1727,11 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
||||
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
||||
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 start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||
&UnixTime::new_only_secs(100),
|
||||
cds::SubmillisPrecision::Absent,
|
||||
@@ -1788,10 +1763,11 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
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, 0, 50);
|
||||
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
||||
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);
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
||||
|
||||
let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||
@@ -1823,11 +1799,12 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
||||
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
let tc_info_2 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
||||
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 200);
|
||||
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);
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 4);
|
||||
|
||||
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||
@@ -1864,9 +1841,10 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
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);
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
||||
let del_res = scheduler.delete_all(&mut pool);
|
||||
assert!(del_res.is_ok());
|
||||
@@ -1875,8 +1853,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, 0, 50);
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 50);
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 2);
|
||||
let del_res = scheduler
|
||||
.delete_by_time_filter(TimeWindow::<cds::CdsTime>::new_select_all(), &mut pool);
|
||||
@@ -1893,10 +1871,13 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
|
||||
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
||||
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 cmd_0_to_delete =
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||
let cmd_1_to_delete =
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
||||
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||
&UnixTime::new_only_secs(100),
|
||||
@@ -1918,10 +1899,13 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
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, 0, 50);
|
||||
let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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);
|
||||
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);
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 3);
|
||||
|
||||
let end_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||
@@ -1944,12 +1928,16 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
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, 0, 50);
|
||||
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
|
||||
let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 150);
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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);
|
||||
let cmd_0_to_delete =
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 100);
|
||||
let cmd_1_to_delete =
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 150);
|
||||
let cmd_out_of_range_1 =
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 200);
|
||||
insert_command_with_release_time(&mut pool, &mut scheduler, u14::ZERO, 200);
|
||||
assert_eq!(scheduler.num_scheduled_telecommands(), 4);
|
||||
|
||||
let start_stamp = cds::CdsTime::from_unix_time_with_u16_days(
|
||||
@@ -1979,16 +1967,17 @@ mod tests {
|
||||
vec![(10, 32), (5, 64)],
|
||||
false,
|
||||
));
|
||||
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
|
||||
let mut scheduler =
|
||||
PusSchedulerAlloc::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, 0, &[]);
|
||||
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, u14::ZERO, &[]);
|
||||
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
|
||||
.expect("insertion failed");
|
||||
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
|
||||
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, u14::new(1), &[]);
|
||||
scheduler
|
||||
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
|
||||
.expect("insertion failed");
|
||||
@@ -2017,12 +2006,12 @@ mod tests {
|
||||
fn test_generic_insert_app_data_test() {
|
||||
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
||||
let sph = SpHeader::new(
|
||||
PacketId::new(PacketType::Tc, true, 0x002),
|
||||
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
|
||||
PacketId::new(PacketType::Tc, true, u11::new(0x002)),
|
||||
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
|
||||
0,
|
||||
);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
|
||||
let mut buf: [u8; 64] = [0; 64];
|
||||
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
|
||||
assert!(result.is_ok());
|
||||
@@ -2039,12 +2028,12 @@ mod tests {
|
||||
fn test_generic_insert_app_data_test_byte_conv_error() {
|
||||
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
||||
let sph = SpHeader::new(
|
||||
PacketId::new(PacketType::Tc, true, 0x002),
|
||||
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
|
||||
PacketId::new(PacketType::Tc, true, u11::new(0x002)),
|
||||
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
|
||||
0,
|
||||
);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
|
||||
let mut buf: [u8; 16] = [0; 16];
|
||||
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
|
||||
assert!(result.is_err());
|
||||
@@ -2068,12 +2057,12 @@ mod tests {
|
||||
fn test_generic_insert_app_data_test_as_vec() {
|
||||
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
|
||||
let sph = SpHeader::new(
|
||||
PacketId::new(PacketType::Tc, true, 0x002),
|
||||
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
|
||||
PacketId::new(PacketType::Tc, true, u11::new(0x002)),
|
||||
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(5)),
|
||||
0,
|
||||
);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, CreatorConfig::default());
|
||||
let mut buf: [u8; 64] = [0; 64];
|
||||
generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc).unwrap();
|
||||
let vec = generate_insert_telecommand_app_data_as_vec(&time_writer, &ping_tc)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::scheduler::PusSchedulerProvider;
|
||||
use super::scheduler::PusScheduler;
|
||||
use super::verification::{VerificationReporter, VerificationReportingProvider};
|
||||
use super::{
|
||||
CacheAndReadRawEcssTc, DirectPusPacketHandlerResult, EcssTcInSharedPoolCacher, EcssTcReceiver,
|
||||
@@ -26,11 +26,11 @@ pub struct PusSchedServiceHandler<
|
||||
TmSender: EcssTmSender,
|
||||
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
PusScheduler: PusSchedulerProvider,
|
||||
PusSchedulerInstance: PusScheduler,
|
||||
> {
|
||||
pub service_helper:
|
||||
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
|
||||
scheduler: PusScheduler,
|
||||
scheduler: PusSchedulerInstance,
|
||||
}
|
||||
|
||||
impl<
|
||||
@@ -38,7 +38,7 @@ impl<
|
||||
TmSender: EcssTmSender,
|
||||
TcInMemConverter: CacheAndReadRawEcssTc,
|
||||
VerificationReporter: VerificationReportingProvider,
|
||||
Scheduler: PusSchedulerProvider,
|
||||
Scheduler: PusScheduler,
|
||||
> PusSchedServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter, Scheduler>
|
||||
{
|
||||
pub fn new(
|
||||
@@ -254,17 +254,19 @@ mod tests {
|
||||
use crate::pus::{DirectPusPacketHandlerResult, MpscTcReceiver, PusPacketHandlingError};
|
||||
use crate::pus::{
|
||||
EcssTcInSharedPoolCacher,
|
||||
scheduler::{self, PusSchedulerProvider, TcInfo},
|
||||
scheduler::{self, PusScheduler, TcInfo},
|
||||
tests::PusServiceHandlerWithSharedStoreCommon,
|
||||
verification::{RequestId, TcStateAccepted, VerificationToken},
|
||||
};
|
||||
use crate::tmtc::PacketSenderWithSharedPool;
|
||||
use alloc::collections::VecDeque;
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
use arbitrary_int::u14;
|
||||
use delegate::delegate;
|
||||
use spacepackets::SpHeader;
|
||||
use spacepackets::ecss::WritablePusPacket;
|
||||
use spacepackets::ecss::scheduling::Subservice;
|
||||
use spacepackets::ecss::tc::PusTcSecondaryHeader;
|
||||
use spacepackets::ecss::{CreatorConfig, WritablePusPacket};
|
||||
use spacepackets::time::TimeWriter;
|
||||
use spacepackets::{
|
||||
ecss::{tc::PusTcCreator, tm::PusTmReader},
|
||||
@@ -347,7 +349,7 @@ mod tests {
|
||||
inserted_tcs: VecDeque<TcInfo>,
|
||||
}
|
||||
|
||||
impl PusSchedulerProvider for TestScheduler {
|
||||
impl PusScheduler for TestScheduler {
|
||||
type TimeProvider = cds::CdsTime;
|
||||
|
||||
fn reset(
|
||||
@@ -386,9 +388,10 @@ mod tests {
|
||||
test_harness: &mut Pus11HandlerWithStoreTester,
|
||||
subservice: Subservice,
|
||||
) {
|
||||
let reply_header = SpHeader::new_for_unseg_tm(TEST_APID, 0, 0);
|
||||
let reply_header = SpHeader::new_for_unseg_tm(TEST_APID, u14::ZERO, 0);
|
||||
let tc_header = PusTcSecondaryHeader::new_simple(11, subservice as u8);
|
||||
let enable_scheduling = PusTcCreator::new(reply_header, tc_header, &[0; 7], true);
|
||||
let enable_scheduling =
|
||||
PusTcCreator::new(reply_header, tc_header, &[0; 7], CreatorConfig::default());
|
||||
let token = test_harness.start_verification(&enable_scheduling);
|
||||
test_harness.send_tc(&token, &enable_scheduling);
|
||||
|
||||
@@ -433,9 +436,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_insert_activity_tc() {
|
||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||
let mut reply_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
||||
let mut reply_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let ping_tc = PusTcCreator::new(reply_header, sec_header, &[], true);
|
||||
let ping_tc = PusTcCreator::new(reply_header, sec_header, &[], CreatorConfig::default());
|
||||
let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc);
|
||||
let stamper = cds::CdsTime::now_with_u16_days().expect("time provider failed");
|
||||
let mut sched_app_data: [u8; 64] = [0; 64];
|
||||
@@ -443,13 +446,13 @@ 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, 1, 0);
|
||||
reply_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8);
|
||||
let enable_scheduling = PusTcCreator::new(
|
||||
reply_header,
|
||||
sec_header,
|
||||
&sched_app_data[..written_len],
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
);
|
||||
let token = test_harness.start_verification(&enable_scheduling);
|
||||
test_harness.send_tc(&token, &enable_scheduling);
|
||||
|
||||
@@ -2,9 +2,11 @@ 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::PusPacket;
|
||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||
use spacepackets::ecss::{CreatorConfig, PusPacket};
|
||||
use std::sync::mpsc;
|
||||
|
||||
use super::verification::{VerificationReporter, VerificationReportingProvider};
|
||||
@@ -75,10 +77,14 @@ impl<
|
||||
// Sequence count will be handled centrally in TM funnel.
|
||||
// It is assumed that the verification reporter was built with a valid APID, so we use
|
||||
// the unchecked API here.
|
||||
let reply_header =
|
||||
SpHeader::new_for_unseg_tm(self.service_helper.verif_reporter().apid(), 0, 0);
|
||||
let reply_header = SpHeader::new_for_unseg_tm(
|
||||
self.service_helper.verif_reporter().apid(),
|
||||
u14::ZERO,
|
||||
0,
|
||||
);
|
||||
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp);
|
||||
let ping_reply = PusTmCreator::new(reply_header, tc_header, &[], true);
|
||||
let ping_reply =
|
||||
PusTmCreator::new(reply_header, tc_header, &[], CreatorConfig::default());
|
||||
if let Err(e) = self
|
||||
.service_helper
|
||||
.common
|
||||
@@ -148,11 +154,13 @@ mod tests {
|
||||
PartialPusHandlingError, PusPacketHandlingError,
|
||||
};
|
||||
use crate::tmtc::PacketSenderWithSharedPool;
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
use arbitrary_int::u14;
|
||||
use delegate::delegate;
|
||||
use spacepackets::SpHeader;
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
|
||||
use spacepackets::ecss::tm::PusTmReader;
|
||||
use spacepackets::ecss::{CreatorConfig, PusPacket};
|
||||
use spacepackets::time::{TimeWriter, cds};
|
||||
|
||||
use super::PusService17TestHandler;
|
||||
@@ -283,9 +291,10 @@ 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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
||||
let ping_tc =
|
||||
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||
let token = test_harness.start_verification(&ping_tc);
|
||||
test_harness.send_tc(&token, &ping_tc);
|
||||
let request_id = token.request_id();
|
||||
@@ -338,9 +347,10 @@ 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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(3, 1);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
||||
let ping_tc =
|
||||
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||
let token = test_harness.start_verification(&ping_tc);
|
||||
test_harness.send_tc(&token, &ping_tc);
|
||||
let result = test_harness.handle_one_tc();
|
||||
@@ -359,9 +369,10 @@ 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, 0, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 200);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
||||
let ping_tc =
|
||||
PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||
let token = test_harness.start_verification(&ping_tc);
|
||||
test_harness.send_tc(&token, &ping_tc);
|
||||
let result = test_harness.handle_one_tc();
|
||||
|
||||
@@ -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};
|
||||
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader, CreatorConfig};
|
||||
//! use spacepackets::ecss::tm::PusTmReader;
|
||||
//! use arbitrary_int::u11;
|
||||
//!
|
||||
//! const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||
//! const TEST_APID: u16 = 0x02;
|
||||
//! const TEST_APID: u11 = u11::new(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).unwrap();
|
||||
//! let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||
//! 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,
|
||||
//! true
|
||||
//! CreatorConfig::default()
|
||||
//! );
|
||||
//! 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::{u3, u11, u14};
|
||||
use core::fmt::{Debug, Display, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::marker::PhantomData;
|
||||
@@ -90,14 +91,14 @@ use core::mem::size_of;
|
||||
use delegate::delegate;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spacepackets::ecss::EcssEnumeration;
|
||||
use spacepackets::SpHeader;
|
||||
use spacepackets::ecss::tc::IsPusTelecommand;
|
||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceCtrl};
|
||||
use spacepackets::{MAX_APID, SpHeader};
|
||||
use spacepackets::ecss::{CreatorConfig, EcssEnumeration};
|
||||
use spacepackets::{ByteConversionError, CcsdsPacket, PacketId, PacketSequenceControl};
|
||||
|
||||
pub use spacepackets::ecss::verification::*;
|
||||
pub use spacepackets::seq_count::SeqCountProviderSimple;
|
||||
pub use spacepackets::seq_count::SequenceCounterSimple;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
@@ -113,9 +114,9 @@ use crate::request::Apid;
|
||||
#[derive(Debug, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct RequestId {
|
||||
version_number: u8,
|
||||
version_number: u3,
|
||||
packet_id: PacketId,
|
||||
psc: PacketSequenceCtrl,
|
||||
psc: PacketSequenceControl,
|
||||
}
|
||||
|
||||
impl Display for RequestId {
|
||||
@@ -157,16 +158,18 @@ impl RequestId {
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> u32 {
|
||||
((self.version_number as u32) << 29)
|
||||
((self.version_number.value() as u32) << 29)
|
||||
| ((self.packet_id.raw() as u32) << 16)
|
||||
| self.psc.raw() as u32
|
||||
}
|
||||
|
||||
pub fn packet_id(&self) -> PacketId {
|
||||
#[inline]
|
||||
pub const fn packet_id(&self) -> PacketId {
|
||||
self.packet_id
|
||||
}
|
||||
|
||||
pub fn packet_seq_ctrl(&self) -> PacketSequenceCtrl {
|
||||
#[inline]
|
||||
pub const fn packet_seq_ctrl(&self) -> PacketSequenceControl {
|
||||
self.psc
|
||||
}
|
||||
|
||||
@@ -181,9 +184,9 @@ impl RequestId {
|
||||
}
|
||||
let raw = u32::from_be_bytes(buf[0..Self::SIZE_AS_BYTES].try_into().unwrap());
|
||||
Some(Self {
|
||||
version_number: ((raw >> 29) & 0b111) as u8,
|
||||
version_number: u3::new(((raw >> 29) & 0b111) as u8),
|
||||
packet_id: PacketId::from(((raw >> 16) & 0xffff) as u16),
|
||||
psc: PacketSequenceCtrl::from((raw & 0xffff) as u16),
|
||||
psc: PacketSequenceControl::from((raw & 0xffff) as u16),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -191,9 +194,9 @@ impl RequestId {
|
||||
impl From<u32> for RequestId {
|
||||
fn from(value: u32) -> Self {
|
||||
Self {
|
||||
version_number: ((value >> 29) & 0b111) as u8,
|
||||
version_number: u3::new(((value >> 29) & 0b111) as u8),
|
||||
packet_id: PacketId::from(((value >> 16) & 0xffff) as u16),
|
||||
psc: PacketSequenceCtrl::from((value & 0xffff) as u16),
|
||||
psc: PacketSequenceControl::from((value & 0xffff) as u16),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,26 +483,19 @@ pub trait VerificationReportingProvider {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VerificationReportCreator {
|
||||
pub dest_id: u16,
|
||||
apid: u16,
|
||||
apid: u11,
|
||||
}
|
||||
|
||||
impl VerificationReportCreator {
|
||||
pub fn new(apid: u16) -> Option<Self> {
|
||||
if apid > MAX_APID {
|
||||
return None;
|
||||
}
|
||||
Some(Self { apid, dest_id: 0 })
|
||||
pub fn new(apid: u11) -> Self {
|
||||
Self { apid, dest_id: 0 }
|
||||
}
|
||||
|
||||
pub fn set_apid(&mut self, apid: u16) -> bool {
|
||||
if apid > MAX_APID {
|
||||
return false;
|
||||
}
|
||||
pub fn set_apid(&mut self, apid: u11) {
|
||||
self.apid = apid;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn apid(&self) -> u16 {
|
||||
pub fn apid(&self) -> u11 {
|
||||
self.apid
|
||||
}
|
||||
|
||||
@@ -522,7 +518,7 @@ impl VerificationReportCreator {
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
subservice: u8,
|
||||
request_id: &RequestId,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
time_stamp: &'time [u8],
|
||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||
@@ -545,7 +541,7 @@ impl VerificationReportCreator {
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
subservice: u8,
|
||||
request_id: &RequestId,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
step: Option<&(impl EcssEnumeration + ?Sized)>,
|
||||
params: &FailParams<'time, '_>,
|
||||
@@ -567,7 +563,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
request_id: &RequestId,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
time_stamp: &'time [u8],
|
||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||
@@ -587,7 +583,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
request_id: &RequestId,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
params: FailParams<'time, '_>,
|
||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||
@@ -609,7 +605,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
request_id: &RequestId,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
time_stamp: &'time [u8],
|
||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||
@@ -632,7 +628,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
request_id: &RequestId,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
params: FailParams<'time, '_>,
|
||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||
@@ -654,7 +650,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
request_id: &RequestId,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
time_stamp: &'time [u8],
|
||||
step: impl EcssEnumeration,
|
||||
@@ -678,7 +674,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
params: FailParamsWithStep<'time, '_>,
|
||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||
@@ -701,7 +697,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
request_id: &RequestId,
|
||||
seq_counter: u16,
|
||||
seq_counter: u14,
|
||||
msg_counter: u16,
|
||||
time_stamp: &'time [u8],
|
||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||
@@ -723,7 +719,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
request_id: &RequestId,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_count: u16,
|
||||
params: FailParams<'time, '_>,
|
||||
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
|
||||
@@ -744,7 +740,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
subservice: u8,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_counter: u16,
|
||||
req_id: &RequestId,
|
||||
time_stamp: &'time [u8],
|
||||
@@ -780,7 +776,7 @@ impl VerificationReportCreator {
|
||||
&self,
|
||||
src_data_buf: &'src_data mut [u8],
|
||||
subservice: u8,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
msg_counter: u16,
|
||||
req_id: &RequestId,
|
||||
step: Option<&(impl EcssEnumeration + ?Sized)>,
|
||||
@@ -832,13 +828,14 @@ impl VerificationReportCreator {
|
||||
sp_header,
|
||||
tm_sec_header,
|
||||
&src_data_buf[0..source_data_len],
|
||||
true,
|
||||
CreatorConfig::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
use spacepackets::ecss::PusError;
|
||||
|
||||
use super::*;
|
||||
@@ -847,7 +844,7 @@ pub mod alloc_mod {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VerificationReporterConfig {
|
||||
apid: u16,
|
||||
apid: u11,
|
||||
pub step_field_width: usize,
|
||||
pub fail_code_field_width: usize,
|
||||
pub max_fail_data_len: usize,
|
||||
@@ -855,20 +852,17 @@ pub mod alloc_mod {
|
||||
|
||||
impl VerificationReporterConfig {
|
||||
pub fn new(
|
||||
apid: u16,
|
||||
apid: u11,
|
||||
step_field_width: usize,
|
||||
fail_code_field_width: usize,
|
||||
max_fail_data_len: usize,
|
||||
) -> Option<Self> {
|
||||
if apid > MAX_APID {
|
||||
return None;
|
||||
}
|
||||
Some(Self {
|
||||
) -> Self {
|
||||
Self {
|
||||
apid,
|
||||
step_field_width,
|
||||
fail_code_field_width,
|
||||
max_fail_data_len,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,7 +874,7 @@ pub mod alloc_mod {
|
||||
fn modify_tm(&self, tm: &mut PusTmCreator);
|
||||
}
|
||||
|
||||
/// [VerificationHookProvider] which does nothing. This is the default hook variant for
|
||||
/// [VerificationHook] 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)]
|
||||
@@ -909,7 +903,7 @@ pub mod alloc_mod {
|
||||
|
||||
impl VerificationReporter<DummyVerificationHook> {
|
||||
pub fn new(owner_id: ComponentId, cfg: &VerificationReporterConfig) -> Self {
|
||||
let reporter = VerificationReportCreator::new(cfg.apid).unwrap();
|
||||
let reporter = VerificationReportCreator::new(cfg.apid);
|
||||
Self {
|
||||
owner_id,
|
||||
source_data_buf: RefCell::new(alloc::vec![
|
||||
@@ -926,14 +920,14 @@ pub mod alloc_mod {
|
||||
}
|
||||
|
||||
impl<VerificationHookInstance: VerificationHook> VerificationReporter<VerificationHookInstance> {
|
||||
/// The provided [VerificationHookProvider] can be used to modify a verification packet
|
||||
/// The provided [VerificationHook] can be used to modify a verification packet
|
||||
/// before it is sent.
|
||||
pub fn new_with_hook(
|
||||
owner_id: ComponentId,
|
||||
cfg: &VerificationReporterConfig,
|
||||
tm_hook: VerificationHookInstance,
|
||||
) -> Self {
|
||||
let reporter = VerificationReportCreator::new(cfg.apid).unwrap();
|
||||
let reporter = VerificationReportCreator::new(cfg.apid);
|
||||
Self {
|
||||
owner_id,
|
||||
source_data_buf: RefCell::new(alloc::vec![
|
||||
@@ -966,8 +960,8 @@ pub mod alloc_mod {
|
||||
|
||||
delegate!(
|
||||
to self.reporter_creator {
|
||||
pub fn set_apid(&mut self, apid: u16) -> bool;
|
||||
pub fn apid(&self) -> u16;
|
||||
pub fn set_apid(&mut self, apid: u11);
|
||||
pub fn apid(&self) -> u11;
|
||||
pub fn dest_id(&self) -> u16;
|
||||
pub fn set_dest_id(&mut self, dest_id: u16);
|
||||
}
|
||||
@@ -1012,7 +1006,7 @@ pub mod alloc_mod {
|
||||
.acceptance_success(
|
||||
source_data_buf.as_mut_slice(),
|
||||
&token.request_id(),
|
||||
0,
|
||||
u14::ZERO,
|
||||
0,
|
||||
time_stamp,
|
||||
)
|
||||
@@ -1032,7 +1026,13 @@ pub mod alloc_mod {
|
||||
let mut buf = self.source_data_buf.borrow_mut();
|
||||
let mut tm_creator = self
|
||||
.reporter_creator
|
||||
.acceptance_failure(buf.as_mut_slice(), &token.request_id(), 0, 0, params)
|
||||
.acceptance_failure(
|
||||
buf.as_mut_slice(),
|
||||
&token.request_id(),
|
||||
u14::ZERO,
|
||||
0,
|
||||
params,
|
||||
)
|
||||
.map_err(PusError::ByteConversion)?;
|
||||
self.tm_hook.modify_tm(&mut tm_creator);
|
||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||
@@ -1051,7 +1051,13 @@ pub mod alloc_mod {
|
||||
let mut buf = self.source_data_buf.borrow_mut();
|
||||
let mut tm_creator = self
|
||||
.reporter_creator
|
||||
.start_success(buf.as_mut_slice(), &token.request_id(), 0, 0, time_stamp)
|
||||
.start_success(
|
||||
buf.as_mut_slice(),
|
||||
&token.request_id(),
|
||||
u14::ZERO,
|
||||
0,
|
||||
time_stamp,
|
||||
)
|
||||
.map_err(PusError::ByteConversion)?;
|
||||
self.tm_hook.modify_tm(&mut tm_creator);
|
||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||
@@ -1071,7 +1077,13 @@ pub mod alloc_mod {
|
||||
let mut buf = self.source_data_buf.borrow_mut();
|
||||
let mut tm_creator = self
|
||||
.reporter_creator
|
||||
.start_failure(buf.as_mut_slice(), &token.request_id(), 0, 0, params)
|
||||
.start_failure(
|
||||
buf.as_mut_slice(),
|
||||
&token.request_id(),
|
||||
u14::ZERO,
|
||||
0,
|
||||
params,
|
||||
)
|
||||
.map_err(PusError::ByteConversion)?;
|
||||
self.tm_hook.modify_tm(&mut tm_creator);
|
||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||
@@ -1094,7 +1106,7 @@ pub mod alloc_mod {
|
||||
.step_success(
|
||||
buf.as_mut_slice(),
|
||||
&token.request_id(),
|
||||
0,
|
||||
u14::ZERO,
|
||||
0,
|
||||
time_stamp,
|
||||
step,
|
||||
@@ -1118,7 +1130,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, 0, 0, params)
|
||||
.step_failure(buf.as_mut_slice(), token, u14::ZERO, 0, params)
|
||||
.map_err(PusError::ByteConversion)?;
|
||||
self.tm_hook.modify_tm(&mut tm_creator);
|
||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||
@@ -1139,7 +1151,13 @@ pub mod alloc_mod {
|
||||
let mut buf = self.source_data_buf.borrow_mut();
|
||||
let mut tm_creator = self
|
||||
.reporter_creator
|
||||
.completion_success(buf.as_mut_slice(), &token.request_id(), 0, 0, time_stamp)
|
||||
.completion_success(
|
||||
buf.as_mut_slice(),
|
||||
&token.request_id(),
|
||||
u14::ZERO,
|
||||
0,
|
||||
time_stamp,
|
||||
)
|
||||
.map_err(PusError::ByteConversion)?;
|
||||
self.tm_hook.modify_tm(&mut tm_creator);
|
||||
sender.send_tm(self.owner_id, PusTmVariant::Direct(tm_creator))?;
|
||||
@@ -1159,7 +1177,13 @@ pub mod alloc_mod {
|
||||
let mut buf = self.source_data_buf.borrow_mut();
|
||||
let mut tm_creator = self
|
||||
.reporter_creator
|
||||
.completion_failure(buf.as_mut_slice(), &token.request_id(), 0, 00, params)
|
||||
.completion_failure(
|
||||
buf.as_mut_slice(),
|
||||
&token.request_id(),
|
||||
u14::ZERO,
|
||||
0,
|
||||
params,
|
||||
)
|
||||
.map_err(PusError::ByteConversion)?;
|
||||
self.tm_hook.modify_tm(&mut tm_creator);
|
||||
sender.send_tm(self.owner_id(), PusTmVariant::Direct(tm_creator))?;
|
||||
@@ -1309,6 +1333,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 core::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
@@ -1370,7 +1395,7 @@ pub mod test_util {
|
||||
fn set_apid(&mut self, _apid: Apid) {}
|
||||
|
||||
fn apid(&self) -> Apid {
|
||||
0
|
||||
Apid::ZERO
|
||||
}
|
||||
|
||||
fn acceptance_success(
|
||||
@@ -1698,13 +1723,15 @@ pub mod tests {
|
||||
};
|
||||
use crate::pus::{ChannelWithId, PusTmVariant};
|
||||
use crate::request::MessageMetadata;
|
||||
use crate::spacepackets::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProvider};
|
||||
use crate::spacepackets::seq_count::{SequenceCounter, SequenceCounterCcsdsSimple};
|
||||
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::{
|
||||
EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumeration, PusError, PusPacket,
|
||||
CreatorConfig, EcssEnumU8, EcssEnumU16, EcssEnumU32, EcssEnumeration, PusError, PusPacket,
|
||||
WritablePusPacket,
|
||||
};
|
||||
use spacepackets::util::UnsignedEnum;
|
||||
@@ -1716,7 +1743,7 @@ pub mod tests {
|
||||
use std::vec::Vec;
|
||||
|
||||
use super::{
|
||||
DummyVerificationHook, FailParamHelper, SeqCountProviderSimple, TcStateAccepted,
|
||||
DummyVerificationHook, FailParamHelper, SequenceCounterSimple, TcStateAccepted,
|
||||
TcStateStarted, VerificationHook, VerificationReportingProvider, WasAtLeastAccepted,
|
||||
handle_completion_failure_with_generic_params,
|
||||
};
|
||||
@@ -1783,13 +1810,13 @@ pub mod tests {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SequenceCounterHook {
|
||||
pub seq_counter: CcsdsSimpleSeqCountProvider,
|
||||
pub msg_counter: SeqCountProviderSimple<u16>,
|
||||
pub seq_counter: SequenceCounterCcsdsSimple,
|
||||
pub msg_counter: SequenceCounterSimple<u16>,
|
||||
}
|
||||
|
||||
impl VerificationHook for SequenceCounterHook {
|
||||
fn modify_tm(&self, tm: &mut spacepackets::ecss::tm::PusTmCreator) {
|
||||
tm.set_seq_count(self.seq_counter.get_and_increment());
|
||||
tm.set_seq_count(u14::new(self.seq_counter.get_and_increment()));
|
||||
tm.set_msg_counter(self.msg_counter.get_and_increment());
|
||||
}
|
||||
}
|
||||
@@ -1805,7 +1832,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).unwrap();
|
||||
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, max_fail_data_len);
|
||||
VerificationReporter::new(id, &cfg)
|
||||
}
|
||||
|
||||
@@ -1813,7 +1840,7 @@ pub mod tests {
|
||||
id: ComponentId,
|
||||
hook: VerificationHookInstance,
|
||||
) -> VerificationReporter<VerificationHookInstance> {
|
||||
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8).unwrap();
|
||||
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||
VerificationReporter::new_with_hook(id, &cfg, hook)
|
||||
}
|
||||
|
||||
@@ -1912,7 +1939,14 @@ pub mod tests {
|
||||
fn check_acceptance_success(&self, timestamp: &[u8; 7]) {
|
||||
let cmp_info = TmInfo {
|
||||
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
||||
common: CommonTmInfo::new(1, TEST_APID, 0, 0, self.reporter.dest_id(), timestamp),
|
||||
common: CommonTmInfo::new(
|
||||
1,
|
||||
TEST_APID,
|
||||
u14::ZERO,
|
||||
0,
|
||||
self.reporter.dest_id(),
|
||||
timestamp,
|
||||
),
|
||||
additional_data: None,
|
||||
};
|
||||
let mut service_queue = self.sender.service_queue.borrow_mut();
|
||||
@@ -1921,7 +1955,7 @@ pub mod tests {
|
||||
assert_eq!(info, cmp_info);
|
||||
}
|
||||
|
||||
fn check_start_success(&mut self, seq_count: u16, msg_counter: u16, timestamp: &[u8]) {
|
||||
fn check_start_success(&mut self, seq_count: u14, msg_counter: u16, timestamp: &[u8]) {
|
||||
let mut srv_queue = self.sender.service_queue.borrow_mut();
|
||||
let cmp_info = TmInfo {
|
||||
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
||||
@@ -1939,7 +1973,7 @@ pub mod tests {
|
||||
assert_eq!(info, cmp_info);
|
||||
}
|
||||
|
||||
fn check_completion_success(&mut self, seq_count: u16, msg_counter: u16) {
|
||||
fn check_completion_success(&mut self, seq_count: u14, msg_counter: u16) {
|
||||
let cmp_info = TmInfo {
|
||||
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
||||
common: CommonTmInfo::new(
|
||||
@@ -1972,7 +2006,14 @@ pub mod tests {
|
||||
fn check_acceptance_failure(&mut self, timestamp: &[u8; 7]) {
|
||||
let cmp_info = TmInfo {
|
||||
requestor: MessageMetadata::new(self.request_id.into(), self.id),
|
||||
common: CommonTmInfo::new(2, TEST_APID, 0, 0, self.reporter.dest_id(), timestamp),
|
||||
common: CommonTmInfo::new(
|
||||
2,
|
||||
TEST_APID,
|
||||
u14::ZERO,
|
||||
0,
|
||||
self.reporter.dest_id(),
|
||||
timestamp,
|
||||
),
|
||||
additional_data: Some([0, 2].to_vec()),
|
||||
};
|
||||
let service_queue = self.sender.service_queue.get_mut();
|
||||
@@ -2060,9 +2101,9 @@ pub mod tests {
|
||||
}
|
||||
|
||||
fn create_generic_ping() -> PusTcCreator<'static> {
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID, 0x34, 0);
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(0x34), 0);
|
||||
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
PusTcCreator::new(sph, tc_header, &[], true)
|
||||
PusTcCreator::new(sph, tc_header, &[], CreatorConfig::default())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2082,8 +2123,8 @@ pub mod tests {
|
||||
fn test_state() {
|
||||
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
|
||||
assert_eq!(testbench.reporter.apid(), TEST_APID);
|
||||
testbench.reporter.set_apid(TEST_APID + 1);
|
||||
assert_eq!(testbench.reporter.apid(), TEST_APID + 1);
|
||||
testbench.reporter.set_apid(u11::new(TEST_APID.value() + 1));
|
||||
assert_eq!(testbench.reporter.apid().value(), TEST_APID.value() + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2233,7 +2274,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(0, 0, &EMPTY_STAMP);
|
||||
testbench.check_start_success(u14::ZERO, 0, &EMPTY_STAMP);
|
||||
testbench.check_step_success(0, &EMPTY_STAMP);
|
||||
testbench.check_step_success(1, &EMPTY_STAMP);
|
||||
}
|
||||
@@ -2267,7 +2308,7 @@ pub mod tests {
|
||||
.step_failure(started_token, fail_params)
|
||||
.expect("Step failure failed");
|
||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||
testbench.check_start_success(0, 0, DUMMY_STAMP);
|
||||
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
|
||||
testbench.check_step_success(0, &EMPTY_STAMP);
|
||||
testbench.check_step_failure(&fail_step, &fail_code, &fail_data_raw);
|
||||
}
|
||||
@@ -2289,7 +2330,7 @@ pub mod tests {
|
||||
.completion_failure(started_token, fail_params)
|
||||
.expect("Completion failure");
|
||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||
testbench.check_start_success(0, 0, DUMMY_STAMP);
|
||||
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
|
||||
|
||||
testbench.check_completion_failure(&fail_code, &[]);
|
||||
}
|
||||
@@ -2309,8 +2350,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(0, 0, DUMMY_STAMP);
|
||||
testbench.check_completion_success(0, 0);
|
||||
testbench.check_start_success(u14::ZERO, 0, DUMMY_STAMP);
|
||||
testbench.check_completion_success(u14::ZERO, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2331,8 +2372,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(1, 1, DUMMY_STAMP);
|
||||
testbench.check_completion_success(2, 2);
|
||||
testbench.check_start_success(u14::new(1), 1, DUMMY_STAMP);
|
||||
testbench.check_completion_success(u14::new(2), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2391,7 +2432,7 @@ pub mod tests {
|
||||
);
|
||||
assert!(result.unwrap());
|
||||
testbench.check_acceptance_success(&EMPTY_STAMP);
|
||||
testbench.check_start_success(0, 0, &EMPTY_STAMP);
|
||||
testbench.check_start_success(u14::ZERO, 0, &EMPTY_STAMP);
|
||||
testbench.check_step_failure(&step, &fail_code, fail_data.as_bytes());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use arbitrary_int::u11;
|
||||
use core::{fmt, marker::PhantomData};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -23,7 +24,7 @@ use crate::{
|
||||
pub type RequestId = u32;
|
||||
|
||||
/// CCSDS APID type definition. Please note that the APID is a 14 bit value.
|
||||
pub type Apid = u16;
|
||||
pub type Apid = u11;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct UniqueApidTargetId {
|
||||
@@ -40,7 +41,7 @@ impl UniqueApidTargetId {
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> ComponentId {
|
||||
((self.apid as u64) << 32) | (self.unique_id as u64)
|
||||
((self.apid.value() as u64) << 32) | (self.unique_id as u64)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> ComponentId {
|
||||
@@ -68,7 +69,7 @@ impl UniqueApidTargetId {
|
||||
impl From<u64> for UniqueApidTargetId {
|
||||
fn from(raw: u64) -> Self {
|
||||
Self {
|
||||
apid: (raw >> 32) as u16,
|
||||
apid: u11::new((raw >> 32) as u16),
|
||||
unique_id: raw as u32,
|
||||
}
|
||||
}
|
||||
@@ -496,14 +497,18 @@ mod tests {
|
||||
use std::sync::mpsc;
|
||||
|
||||
use alloc::string::ToString;
|
||||
use arbitrary_int::{u11, u14};
|
||||
use spacepackets::{
|
||||
ByteConversionError, SpHeader,
|
||||
ecss::tc::{PusTcCreator, PusTcSecondaryHeader},
|
||||
ecss::{
|
||||
CreatorConfig,
|
||||
tc::{PusTcCreator, PusTcSecondaryHeader},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
queue::{GenericReceiveError, GenericSendError},
|
||||
request::{MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
|
||||
request::{Apid, MessageMetadata, MessageSenderMap, MessageSenderStoreProvider},
|
||||
};
|
||||
|
||||
use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};
|
||||
@@ -514,8 +519,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_basic_target_id_with_apid() {
|
||||
let id = UniqueApidTargetId::new(0x111, 0x01);
|
||||
assert_eq!(id.apid, 0x111);
|
||||
let id = UniqueApidTargetId::new(Apid::new(0x111), 0x01);
|
||||
assert_eq!(id.apid.value(), 0x111);
|
||||
assert_eq!(id.unique_id, 0x01);
|
||||
assert_eq!(id.id(), id.raw());
|
||||
assert_eq!(u64::from(id), id.raw());
|
||||
@@ -532,19 +537,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_basic_target_id_with_apid_from_pus_tc() {
|
||||
let sp_header = SpHeader::new_for_unseg_tc(0x111, 5, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(u11::new(0x111), u14::new(5), 0);
|
||||
let app_data = 1_u32.to_be_bytes();
|
||||
let pus_tc = PusTcCreator::new_simple(sp_header, 17, 1, &app_data, true);
|
||||
let pus_tc =
|
||||
PusTcCreator::new_simple(sp_header, 17, 1, &app_data, CreatorConfig::default());
|
||||
let id = UniqueApidTargetId::from_pus_tc(&pus_tc).unwrap();
|
||||
assert_eq!(id.apid, 0x111);
|
||||
assert_eq!(id.apid.value(), 0x111);
|
||||
assert_eq!(id.unique_id, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_target_id_with_apid_from_pus_tc_invalid_app_data() {
|
||||
let sp_header = SpHeader::new_for_unseg_tc(0x111, 5, 0);
|
||||
let sp_header = SpHeader::new_for_unseg_tc(u11::new(0x111), u14::new(5), 0);
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let pus_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
|
||||
let pus_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, CreatorConfig::default());
|
||||
let error = UniqueApidTargetId::from_pus_tc(&pus_tc);
|
||||
assert!(error.is_err());
|
||||
let error = error.unwrap_err();
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use arbitrary_int::{u11, u14};
|
||||
use spacepackets::SpHeader;
|
||||
use spacepackets::ecss::CreatorConfig;
|
||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||
use spacepackets::time::TimeWriter;
|
||||
use spacepackets::time::cds::CdsTime;
|
||||
|
||||
pub struct PusTmWithCdsShortHelper {
|
||||
apid: u16,
|
||||
apid: u11,
|
||||
cds_short_buf: [u8; 7],
|
||||
}
|
||||
|
||||
impl PusTmWithCdsShortHelper {
|
||||
pub fn new(apid: u16) -> Self {
|
||||
pub fn new(apid: u11) -> Self {
|
||||
Self {
|
||||
apid,
|
||||
cds_short_buf: [0; 7],
|
||||
@@ -22,7 +23,7 @@ impl PusTmWithCdsShortHelper {
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
let time_stamp = CdsTime::now_with_u16_days().unwrap();
|
||||
time_stamp.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
||||
@@ -35,7 +36,7 @@ impl PusTmWithCdsShortHelper {
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
stamper: &CdsTime,
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
stamper.write_to_bytes(&mut self.cds_short_buf).unwrap();
|
||||
self.create_pus_tm_common(service, subservice, source_data, seq_count)
|
||||
@@ -46,40 +47,47 @@ impl PusTmWithCdsShortHelper {
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_data: &'data [u8],
|
||||
seq_count: u16,
|
||||
seq_count: u14,
|
||||
) -> PusTmCreator<'_, 'data> {
|
||||
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
|
||||
let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf);
|
||||
PusTmCreator::new(reply_header, tc_header, source_data, true)
|
||||
PusTmCreator::new(
|
||||
reply_header,
|
||||
tc_header,
|
||||
source_data,
|
||||
CreatorConfig::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use arbitrary_int::{u11, u14};
|
||||
use spacepackets::{CcsdsPacket, ecss::PusPacket, time::cds::CdsTime};
|
||||
|
||||
use super::PusTmWithCdsShortHelper;
|
||||
|
||||
#[test]
|
||||
fn test_helper_with_stamper() {
|
||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123);
|
||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(u11::new(0x123));
|
||||
let stamper = CdsTime::new_with_u16_days(0, 0);
|
||||
let tm = pus_tm_helper.create_pus_tm_with_stamper(17, 1, &[1, 2, 3, 4], &stamper, 25);
|
||||
let tm =
|
||||
pus_tm_helper.create_pus_tm_with_stamper(17, 1, &[1, 2, 3, 4], &stamper, u14::new(25));
|
||||
assert_eq!(tm.service(), 17);
|
||||
assert_eq!(tm.subservice(), 1);
|
||||
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
||||
assert_eq!(tm.seq_count(), 25);
|
||||
assert_eq!(tm.seq_count().value(), 25);
|
||||
assert_eq!(tm.timestamp(), [64, 0, 0, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_helper_from_now() {
|
||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123);
|
||||
let tm = pus_tm_helper.create_pus_tm_timestamp_now(17, 1, &[1, 2, 3, 4], 25);
|
||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(u11::new(0x123));
|
||||
let tm = pus_tm_helper.create_pus_tm_timestamp_now(17, 1, &[1, 2, 3, 4], u14::new(25));
|
||||
assert_eq!(tm.service(), 17);
|
||||
assert_eq!(tm.subservice(), 1);
|
||||
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
||||
assert_eq!(tm.seq_count(), 25);
|
||||
assert_eq!(tm.seq_count().value(), 25);
|
||||
assert_eq!(tm.timestamp().len(), 7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
use core::mem::size_of;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spacepackets::ecss::{PfcReal, PfcUnsigned, Ptc};
|
||||
use spacepackets::time::CcsdsTimeProvider;
|
||||
use spacepackets::time::cds::CdsTime;
|
||||
use spacepackets::time::{CcsdsTimeProvider, TimeWriter};
|
||||
|
||||
enum NumOfParamsInfo {
|
||||
/// The parameter entry is a scalar field
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
#![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,22 +1,23 @@
|
||||
use satrs::event_man::{
|
||||
use arbitrary_int::u11;
|
||||
use satrs::event_man_legacy::{
|
||||
EventManagerWithMpsc, EventMessage, EventMessageU32, EventRoutingError, EventSendProvider,
|
||||
EventU32SenderMpsc,
|
||||
};
|
||||
use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
|
||||
use satrs::events_legacy::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
|
||||
use satrs::params::U32Pair;
|
||||
use satrs::params::{Params, ParamsHeapless, WritableToBeBytes};
|
||||
use satrs::pus::event_man::{DefaultPusEventReportingMap, EventReporter, PusEventTmCreatorWithMap};
|
||||
use satrs::request::UniqueApidTargetId;
|
||||
use satrs::tmtc::PacketAsVec;
|
||||
use spacepackets::ecss::PusError;
|
||||
use spacepackets::ecss::tm::PusTmReader;
|
||||
use spacepackets::ecss::{PusError, PusPacket};
|
||||
use std::sync::mpsc::{self, SendError, TryRecvError};
|
||||
use std::thread;
|
||||
|
||||
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(1, 0);
|
||||
const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5);
|
||||
const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||
const TEST_APID: u16 = 0x02;
|
||||
const TEST_APID: u11 = u11::new(0x02);
|
||||
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -35,8 +36,7 @@ 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(), 0x02, 0, 128).expect("Creating event reporter failed");
|
||||
let reporter = EventReporter::new(TEST_ID.raw(), u11::new(0x02), 0, 128);
|
||||
let pus_event_man =
|
||||
PusEventTmCreatorWithMap::new(reporter, DefaultPusEventReportingMap::default());
|
||||
let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#[cfg(feature = "crossbeam")]
|
||||
pub mod crossbeam_test {
|
||||
use arbitrary_int::traits::Integer as _;
|
||||
use arbitrary_int::u14;
|
||||
use hashbrown::HashMap;
|
||||
use satrs::pool::{PoolProvider, PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
|
||||
use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
|
||||
@@ -11,7 +13,7 @@ pub mod crossbeam_test {
|
||||
use spacepackets::SpHeader;
|
||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
||||
use spacepackets::ecss::tm::PusTmReader;
|
||||
use spacepackets::ecss::{EcssEnumU8, EcssEnumU16, PusPacket, WritablePusPacket};
|
||||
use spacepackets::ecss::{CreatorConfig, EcssEnumU8, EcssEnumU16, WritablePusPacket};
|
||||
use std::sync::RwLock;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -31,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).unwrap();
|
||||
let cfg = VerificationReporterConfig::new(TEST_APID, 1, 2, 8);
|
||||
// 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)],
|
||||
@@ -57,9 +59,9 @@ pub mod crossbeam_test {
|
||||
let (tx_tc_1, rx_tc_1) = crossbeam_channel::bounded(3);
|
||||
{
|
||||
let mut tc_guard = shared_tc_pool.write().unwrap();
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID, u14::ZERO, 0);
|
||||
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let pus_tc_0 = PusTcCreator::new_no_app_data(sph, tc_header, true);
|
||||
let pus_tc_0 = PusTcCreator::new_no_app_data(sph, tc_header, CreatorConfig::default());
|
||||
req_id_0 = RequestId::new(&pus_tc_0);
|
||||
let addr = tc_guard
|
||||
.free_element(pus_tc_0.len_written(), |buf| {
|
||||
@@ -67,9 +69,9 @@ pub mod crossbeam_test {
|
||||
})
|
||||
.unwrap();
|
||||
tx_tc_0.send(addr).unwrap();
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID, 1, 0);
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID, u14::new(1), 0);
|
||||
let tc_header = PusTcSecondaryHeader::new_simple(5, 1);
|
||||
let pus_tc_1 = PusTcCreator::new_no_app_data(sph, tc_header, true);
|
||||
let pus_tc_1 = PusTcCreator::new_no_app_data(sph, tc_header, CreatorConfig::default());
|
||||
req_id_1 = RequestId::new(&pus_tc_1);
|
||||
let addr = tc_guard
|
||||
.free_element(pus_tc_0.len_written(), |buf| {
|
||||
|
||||
@@ -21,6 +21,7 @@ use std::{
|
||||
thread,
|
||||
};
|
||||
|
||||
use arbitrary_int::{u11, u14};
|
||||
use hashbrown::HashSet;
|
||||
use satrs::{
|
||||
ComponentId,
|
||||
@@ -36,7 +37,7 @@ use satrs::{
|
||||
};
|
||||
use spacepackets::{
|
||||
CcsdsPacket, PacketId, SpHeader,
|
||||
ecss::{WritablePusPacket, tc::PusTcCreator},
|
||||
ecss::{CreatorConfig, WritablePusPacket, tc::PusTcCreator},
|
||||
};
|
||||
use std::{collections::VecDeque, sync::Arc, vec::Vec};
|
||||
|
||||
@@ -192,7 +193,7 @@ fn test_cobs_server() {
|
||||
matches!(tc_receiver.try_recv(), Err(mpsc::TryRecvError::Empty));
|
||||
}
|
||||
|
||||
const TEST_APID_0: u16 = 0x02;
|
||||
const TEST_APID_0: u11 = u11::new(0x02);
|
||||
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -217,8 +218,8 @@ impl SpacePacketValidator for SimpleVerificator {
|
||||
fn test_ccsds_server() {
|
||||
let (tc_sender, tc_receiver) = mpsc::channel();
|
||||
let mut tm_source = SyncTmSource::default();
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, 0, 0);
|
||||
let verif_tm = PusTcCreator::new_simple(sph, 1, 1, &[], true);
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, u14::new(0), 0);
|
||||
let verif_tm = PusTcCreator::new_simple(sph, 1, 1, &[], CreatorConfig::default());
|
||||
let tm_0 = verif_tm.to_vec().expect("tm generation failed");
|
||||
tm_source.add_tm(&tm_0);
|
||||
let mut packet_id_lookup = SimpleVerificator::default();
|
||||
@@ -267,8 +268,8 @@ fn test_ccsds_server() {
|
||||
.expect("setting reas timeout failed");
|
||||
|
||||
// Send ping telecommand.
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, 0, 0);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
|
||||
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, u14::new(0), 0);
|
||||
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], CreatorConfig::default());
|
||||
let tc_0 = ping_tc.to_vec().expect("packet creation failed");
|
||||
stream
|
||||
.write_all(&tc_0)
|
||||
|
||||
Reference in New Issue
Block a user