Merge remote-tracking branch 'origin/main' into add-timestamp-to-sim-request
Some checks are pending
Rust/sat-rs/pipeline/pr-main Build queued...

This commit is contained in:
Robin Müller 2024-05-08 14:57:12 +02:00
commit d2fc783562
39 changed files with 1755 additions and 1275 deletions

64
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: ci
on: [push, pull_request]
jobs:
check:
name: Check build
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --release
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install nextest
uses: taiki-e/install-action@nextest
- run: cargo nextest run --all-features
- run: cargo test --doc
cross-check:
name: Check Cross-Compilation
runs-on: ubuntu-latest
strategy:
matrix:
target:
- armv7-unknown-linux-gnueabihf
- thumbv7em-none-eabihf
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf"
- run: cargo check -p satrs --release --target=${{matrix.target}} --no-default-features
fmt:
name: Check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo fmt --all -- --check
docs:
name: Check Documentation Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo clippy -- -D warnings

View File

@ -33,6 +33,7 @@ pipeline {
stage('Test') { stage('Test') {
steps { steps {
sh 'cargo nextest r --all-features' sh 'cargo nextest r --all-features'
sh 'cargo test --doc'
} }
} }
stage('Check with all features') { stage('Check with all features') {

View File

@ -1,14 +1,17 @@
# Events # Events
Events can be an extremely important mechanism used for remote systems to monitor unexpected Events are an important mechanism used for remote systems to monitor unexpected
or expected anomalies and events occuring on these systems. They are oftentimes tied to or expected anomalies and events occuring on these systems.
One common use case for events on remote systems is to offer a light-weight publish-subscribe
mechanism and IPC mechanism for software and hardware events which are also packaged as telemetry
(TM) or can trigger a system response. They can also be tied to
Fault Detection, Isolation and Recovery (FDIR) operations, which need to happen autonomously. Fault Detection, Isolation and Recovery (FDIR) operations, which need to happen autonomously.
Events can also be used as a convenient Inter-Process Communication (IPC) mechansism, which is The PUS Service 5 standardizes how the ground interface for events might look like, but does not
also observable for the Ground segment. The PUS Service 5 standardizes how the ground interface specify how other software components might react to those events. There is the PUS Service 19,
for events might look like, but does not specify how other software components might react which might be used for that purpose, but the event components recommended by this framework do not
to those events. There is the PUS Service 19, which might be used for that purpose, but the rely on the present of this service.
event components recommended by this framework do not really need this service.
The following images shows how the flow of events could look like in a system where components The following images shows how the flow of events could look like in a system where components
can generate events, and where other system components might be interested in those events: can generate events, and where other system components might be interested in those events:

View File

@ -10,6 +10,7 @@ class Apid(enum.IntEnum):
GENERIC_PUS = 2 GENERIC_PUS = 2
ACS = 3 ACS = 3
CFDP = 4 CFDP = 4
TMTC = 5
class EventSeverity(enum.IntEnum): class EventSeverity(enum.IntEnum):

View File

@ -144,7 +144,9 @@ class PusHandler(GenericApidHandlerBase):
) )
src_data = tm_packet.source_data src_data = tm_packet.source_data
event_u32 = EventU32.unpack(src_data) event_u32 = EventU32.unpack(src_data)
_LOGGER.info(f"Received event packet. Event: {event_u32}") _LOGGER.info(
f"Received event packet. Source APID: {Apid(tm_packet.apid)!r}, Event: {event_u32}"
)
if event_u32.group_id == 0 and event_u32.unique_id == 0: if event_u32.group_id == 0 and event_u32.unique_id == 0:
_LOGGER.info("Received test event") _LOGGER.info("Received test event")
elif service == 17: elif service == 17:

View File

@ -38,8 +38,7 @@ pub enum GroupId {
pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::UNSPECIFIED; pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::UNSPECIFIED;
pub const SERVER_PORT: u16 = 7301; pub const SERVER_PORT: u16 = 7301;
pub const TEST_EVENT: EventU32TypedSev<SeverityInfo> = pub const TEST_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(0, 0);
EventU32TypedSev::<SeverityInfo>::const_new(0, 0);
lazy_static! { lazy_static! {
pub static ref PACKET_ID_VALIDATOR: HashSet<PacketId> = { pub static ref PACKET_ID_VALIDATOR: HashSet<PacketId> = {

View File

@ -2,19 +2,15 @@ use std::sync::mpsc::{self};
use crate::pus::create_verification_reporter; use crate::pus::create_verification_reporter;
use satrs::event_man::{EventMessageU32, EventRoutingError}; use satrs::event_man::{EventMessageU32, EventRoutingError};
use satrs::params::WritableToBeBytes;
use satrs::pus::event::EventTmHookProvider; use satrs::pus::event::EventTmHookProvider;
use satrs::pus::verification::VerificationReporter; use satrs::pus::verification::VerificationReporter;
use satrs::pus::EcssTmSender; use satrs::pus::EcssTmSender;
use satrs::request::UniqueApidTargetId; use satrs::request::UniqueApidTargetId;
use satrs::{ use satrs::{
event_man::{ event_man::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded},
EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded,
MpscEventReceiver,
},
pus::{ pus::{
event_man::{ event_man::{
DefaultPusEventU32Dispatcher, EventReporter, EventRequest, EventRequestWithToken, DefaultPusEventU32TmCreator, EventReporter, EventRequest, EventRequestWithToken,
}, },
verification::{TcStateStarted, VerificationReportingProvider, VerificationToken}, verification::{TcStateStarted, VerificationReportingProvider, VerificationToken},
}, },
@ -40,13 +36,13 @@ impl EventTmHookProvider for EventApidSetter {
/// packets. It also handles the verification completion of PUS event service requests. /// packets. It also handles the verification completion of PUS event service requests.
pub struct PusEventHandler<TmSender: EcssTmSender> { pub struct PusEventHandler<TmSender: EcssTmSender> {
event_request_rx: mpsc::Receiver<EventRequestWithToken>, event_request_rx: mpsc::Receiver<EventRequestWithToken>,
pus_event_dispatcher: DefaultPusEventU32Dispatcher<()>, pus_event_tm_creator: DefaultPusEventU32TmCreator<EventApidSetter>,
pus_event_man_rx: mpsc::Receiver<EventMessageU32>, pus_event_man_rx: mpsc::Receiver<EventMessageU32>,
tm_sender: TmSender, tm_sender: TmSender,
time_provider: CdsTime, time_provider: CdsTime,
timestamp: [u8; 7], timestamp: [u8; 7],
small_data_buf: [u8; 64],
verif_handler: VerificationReporter, verif_handler: VerificationReporter,
event_apid_setter: EventApidSetter,
} }
impl<TmSender: EcssTmSender> PusEventHandler<TmSender> { impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
@ -61,9 +57,16 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
// All events sent to the manager are routed to the PUS event manager, which generates PUS event // All events sent to the manager are routed to the PUS event manager, which generates PUS event
// telemetry for each event. // telemetry for each event.
let event_reporter = EventReporter::new(PUS_EVENT_MANAGEMENT.raw(), 0, 0, 128).unwrap(); let event_reporter = EventReporter::new_with_hook(
PUS_EVENT_MANAGEMENT.raw(),
0,
0,
128,
EventApidSetter::default(),
)
.unwrap();
let pus_event_dispatcher = let pus_event_dispatcher =
DefaultPusEventU32Dispatcher::new_with_default_backend(event_reporter); DefaultPusEventU32TmCreator::new_with_default_backend(event_reporter);
let pus_event_man_send_provider = EventU32SenderMpscBounded::new( let pus_event_man_send_provider = EventU32SenderMpscBounded::new(
PUS_EVENT_MANAGEMENT.raw(), PUS_EVENT_MANAGEMENT.raw(),
pus_event_man_tx, pus_event_man_tx,
@ -75,13 +78,13 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
Self { Self {
event_request_rx, event_request_rx,
pus_event_dispatcher, pus_event_tm_creator: pus_event_dispatcher,
pus_event_man_rx, pus_event_man_rx,
time_provider: CdsTime::new_with_u16_days(0, 0), time_provider: CdsTime::new_with_u16_days(0, 0),
timestamp: [0; 7], timestamp: [0; 7],
small_data_buf: [0; 64],
verif_handler, verif_handler,
tm_sender, tm_sender,
event_apid_setter: EventApidSetter::default(),
} }
} }
@ -95,75 +98,110 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
.completion_success(&self.tm_sender, started_token, timestamp) .completion_success(&self.tm_sender, started_token, timestamp)
.expect("Sending completion success failed"); .expect("Sending completion success failed");
}; };
// handle event requests loop {
if let Ok(event_req) = self.event_request_rx.try_recv() { // handle event requests
match event_req.request { match self.event_request_rx.try_recv() {
EventRequest::Enable(event) => { Ok(event_req) => match event_req.request {
self.pus_event_dispatcher EventRequest::Enable(event) => {
.enable_tm_for_event(&event) self.pus_event_tm_creator
.expect("Enabling TM failed"); .enable_tm_for_event(&event)
update_time(&mut self.time_provider, &mut self.timestamp); .expect("Enabling TM failed");
report_completion(event_req, &self.timestamp); update_time(&mut self.time_provider, &mut self.timestamp);
} report_completion(event_req, &self.timestamp);
EventRequest::Disable(event) => { }
self.pus_event_dispatcher EventRequest::Disable(event) => {
.disable_tm_for_event(&event) self.pus_event_tm_creator
.expect("Disabling TM failed"); .disable_tm_for_event(&event)
update_time(&mut self.time_provider, &mut self.timestamp); .expect("Disabling TM failed");
report_completion(event_req, &self.timestamp); update_time(&mut self.time_provider, &mut self.timestamp);
} report_completion(event_req, &self.timestamp);
}
},
Err(e) => match e {
mpsc::TryRecvError::Empty => break,
mpsc::TryRecvError::Disconnected => {
log::warn!("all event request senders have disconnected");
break;
}
},
} }
} }
} }
pub fn generate_pus_event_tm(&mut self) { pub fn generate_pus_event_tm(&mut self) {
// Perform the generation of PUS event packets loop {
if let Ok(event_msg) = self.pus_event_man_rx.try_recv() { // Perform the generation of PUS event packets
update_time(&mut self.time_provider, &mut self.timestamp); match self.pus_event_man_rx.try_recv() {
let param_vec = event_msg.params().map_or(Vec::new(), |param| { Ok(event_msg) => {
param.to_vec().expect("failed to convert params to vec") // We use the TM modification hook to set the sender APID for each event.
}); self.pus_event_tm_creator.reporter.tm_hook.next_apid =
self.event_apid_setter.next_apid = UniqueApidTargetId::from(event_msg.sender_id()).apid; UniqueApidTargetId::from(event_msg.sender_id()).apid;
self.pus_event_dispatcher update_time(&mut self.time_provider, &mut self.timestamp);
.generate_pus_event_tm_generic( let generation_result = self
&self.tm_sender, .pus_event_tm_creator
&self.timestamp, .generate_pus_event_tm_generic_with_generic_params(
event_msg.event(), &self.tm_sender,
Some(&param_vec), &self.timestamp,
) event_msg.event(),
.expect("Sending TM as event failed"); &mut self.small_data_buf,
event_msg.params(),
)
.expect("Sending TM as event failed");
if !generation_result.params_were_propagated {
log::warn!(
"Event TM parameters were not propagated: {:?}",
event_msg.params()
);
}
}
Err(e) => match e {
mpsc::TryRecvError::Empty => break,
mpsc::TryRecvError::Disconnected => {
log::warn!("All event senders have disconnected");
break;
}
},
}
} }
} }
} }
/// This is a thin wrapper around the event manager which also caches the sender component pub struct EventHandler<TmSender: EcssTmSender> {
/// used to send events to the event manager. pub pus_event_handler: PusEventHandler<TmSender>,
pub struct EventManagerWrapper {
event_manager: EventManagerWithBoundedMpsc, event_manager: EventManagerWithBoundedMpsc,
event_sender: mpsc::Sender<EventMessageU32>,
} }
impl EventManagerWrapper { impl<TmSender: EcssTmSender> EventHandler<TmSender> {
pub fn new() -> Self { pub fn new(
// The sender handle is the primary sender handle for all components which want to create events. tm_sender: TmSender,
// The event manager will receive the RX handle to receive all the events. event_rx: mpsc::Receiver<EventMessageU32>,
let (event_sender, event_man_rx) = mpsc::channel(); event_request_rx: mpsc::Receiver<EventRequestWithToken>,
let event_recv = MpscEventReceiver::new(event_man_rx); ) -> Self {
let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
let pus_event_handler = PusEventHandler::new(
tm_sender,
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid),
&mut event_manager,
event_request_rx,
);
Self { Self {
event_manager: EventManagerWithBoundedMpsc::new(event_recv), pus_event_handler,
event_sender, event_manager,
} }
} }
// Returns a cached event sender to send events to the event manager for routing. #[allow(dead_code)]
pub fn clone_event_sender(&self) -> mpsc::Sender<EventMessageU32> {
self.event_sender.clone()
}
pub fn event_manager(&mut self) -> &mut EventManagerWithBoundedMpsc { pub fn event_manager(&mut self) -> &mut EventManagerWithBoundedMpsc {
&mut self.event_manager &mut self.event_manager
} }
pub fn periodic_operation(&mut self) {
self.pus_event_handler.handle_event_requests();
self.try_event_routing();
self.pus_event_handler.generate_pus_event_tm();
}
pub fn try_event_routing(&mut self) { pub fn try_event_routing(&mut self) {
let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| { let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| {
self.routing_error_handler(event_msg, error) self.routing_error_handler(event_msg, error)
@ -177,41 +215,83 @@ impl EventManagerWrapper {
} }
} }
pub struct EventHandler<TmSender: EcssTmSender> { #[cfg(test)]
pub event_man_wrapper: EventManagerWrapper, mod tests {
pub pus_event_handler: PusEventHandler<TmSender>, use satrs::{
} events::EventU32,
pus::verification::VerificationReporterCfg,
spacepackets::{
ecss::{tm::PusTmReader, PusPacket},
CcsdsPacket,
},
tmtc::PacketAsVec,
};
impl<TmSender: EcssTmSender> EventHandler<TmSender> { use super::*;
pub fn new(
tm_sender: TmSender, const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(1, 2);
event_request_rx: mpsc::Receiver<EventRequestWithToken>, const TEST_EVENT: EventU32 = EventU32::new(satrs::events::Severity::Info, 1, 1);
) -> Self {
let mut event_man_wrapper = EventManagerWrapper::new(); pub struct EventManagementTestbench {
let pus_event_handler = PusEventHandler::new( pub event_tx: mpsc::SyncSender<EventMessageU32>,
tm_sender, pub event_manager: EventManagerWithBoundedMpsc,
create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid), pub tm_receiver: mpsc::Receiver<PacketAsVec>,
event_man_wrapper.event_manager(), pub pus_event_handler: PusEventHandler<mpsc::Sender<PacketAsVec>>,
event_request_rx, }
);
Self { impl EventManagementTestbench {
event_man_wrapper, pub fn new() -> Self {
pus_event_handler, 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 = VerificationReporterCfg::new(0x05, 2, 2, 128).unwrap();
let verif_reporter =
VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg);
let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx);
let pus_event_handler = PusEventHandler::<mpsc::Sender<PacketAsVec>>::new(
tm_sender,
verif_reporter,
&mut event_manager,
event_req_rx,
);
Self {
event_tx,
tm_receiver,
event_manager,
pus_event_handler,
}
} }
} }
pub fn clone_event_sender(&self) -> mpsc::Sender<EventMessageU32> { #[test]
self.event_man_wrapper.clone_event_sender() fn test_basic_event_generation() {
let mut testbench = EventManagementTestbench::new();
testbench
.event_tx
.send(EventMessageU32::new(
TEST_CREATOR_ID.id(),
EventU32::new(satrs::events::Severity::Info, 1, 1),
))
.expect("failed to send event");
testbench.pus_event_handler.handle_event_requests();
testbench.event_manager.try_event_handling(|_, _| {});
testbench.pus_event_handler.generate_pus_event_tm();
let tm_packet = testbench
.tm_receiver
.try_recv()
.expect("failed to receive TM packet");
assert_eq!(tm_packet.sender_id, PUS_EVENT_MANAGEMENT.id());
let tm_reader = PusTmReader::new(&tm_packet.packet, 7)
.expect("failed to create TM reader")
.0;
assert_eq!(tm_reader.apid(), TEST_CREATOR_ID.apid);
assert_eq!(tm_reader.user_data().len(), 4);
let event_read_back = EventU32::from_be_bytes(tm_reader.user_data().try_into().unwrap());
assert_eq!(event_read_back, TEST_EVENT);
} }
#[allow(dead_code)] #[test]
pub fn event_manager(&mut self) -> &mut EventManagerWithBoundedMpsc { fn test_basic_event_disabled() {
self.event_man_wrapper.event_manager() // TODO: Add test.
}
pub fn periodic_operation(&mut self) {
self.pus_event_handler.handle_event_requests();
self.event_man_wrapper.try_event_routing();
self.pus_event_handler.generate_pus_event_tm();
} }
} }

View File

@ -3,14 +3,13 @@ use std::net::{SocketAddr, UdpSocket};
use std::sync::mpsc; use std::sync::mpsc;
use log::{info, warn}; use log::{info, warn};
use satrs::pus::HandlingStatus;
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderRaw}; use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderRaw};
use satrs::{ use satrs::{
hal::std::udp_server::{ReceiveResult, UdpTcServer}, hal::std::udp_server::{ReceiveResult, UdpTcServer},
pool::{PoolProviderWithGuards, SharedStaticMemoryPool}, pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
}; };
use crate::pus::HandlingStatus;
pub trait UdpTmHandler { pub trait UdpTmHandler {
fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr); fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr);
} }

View File

@ -11,7 +11,7 @@ use crate::events::EventHandler;
use crate::interface::udp::DynamicUdpTmHandler; use crate::interface::udp::DynamicUdpTmHandler;
use crate::pus::stack::PusStack; use crate::pus::stack::PusStack;
use crate::tmtc::tc_source::{TcSourceTaskDynamic, TcSourceTaskStatic}; use crate::tmtc::tc_source::{TcSourceTaskDynamic, TcSourceTaskStatic};
use crate::tmtc::tm_sink::{TmFunnelDynamic, TmFunnelStatic}; use crate::tmtc::tm_sink::{TmSinkDynamic, TmSinkStatic};
use log::info; use log::info;
use pus::test::create_test_service_dynamic; use pus::test::create_test_service_dynamic;
use satrs::hal::std::tcp_server::ServerConfig; use satrs::hal::std::tcp_server::ServerConfig;
@ -54,11 +54,11 @@ fn static_tmtc_pool_main() {
let shared_tm_pool_wrapper = SharedPacketPool::new(&shared_tm_pool); let shared_tm_pool_wrapper = SharedPacketPool::new(&shared_tm_pool);
let shared_tc_pool_wrapper = SharedPacketPool::new(&shared_tc_pool); let shared_tc_pool_wrapper = SharedPacketPool::new(&shared_tc_pool);
let (tc_source_tx, tc_source_rx) = mpsc::sync_channel(50); let (tc_source_tx, tc_source_rx) = mpsc::sync_channel(50);
let (tm_funnel_tx, tm_funnel_rx) = mpsc::sync_channel(50); let (tm_sink_tx, tm_sink_rx) = mpsc::sync_channel(50);
let (tm_server_tx, tm_server_rx) = mpsc::sync_channel(50); let (tm_server_tx, tm_server_rx) = mpsc::sync_channel(50);
let tm_funnel_tx_sender = let tm_sink_tx_sender =
PacketSenderWithSharedPool::new(tm_funnel_tx.clone(), shared_tm_pool_wrapper.clone()); PacketSenderWithSharedPool::new(tm_sink_tx.clone(), shared_tm_pool_wrapper.clone());
let (mgm_handler_composite_tx, mgm_handler_composite_rx) = let (mgm_handler_composite_tx, mgm_handler_composite_rx) =
mpsc::channel::<GenericMessage<CompositeRequest>>(); mpsc::channel::<GenericMessage<CompositeRequest>>();
@ -80,11 +80,12 @@ fn static_tmtc_pool_main() {
// Create event handling components // Create event handling components
// These sender handles are used to send event requests, for example to enable or disable // These sender handles are used to send event requests, for example to enable or disable
// certain events. // certain events.
let (event_tx, event_rx) = mpsc::sync_channel(100);
let (event_request_tx, event_request_rx) = mpsc::channel::<EventRequestWithToken>(); let (event_request_tx, event_request_rx) = mpsc::channel::<EventRequestWithToken>();
// The event task is the core handler to perform the event routing and TM handling as specified // The event task is the core handler to perform the event routing and TM handling as specified
// in the sat-rs documentation. // in the sat-rs documentation.
let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_request_rx); let mut event_handler = EventHandler::new(tm_sink_tx.clone(), event_rx, event_request_rx);
let (pus_test_tx, pus_test_rx) = mpsc::channel(); let (pus_test_tx, pus_test_rx) = mpsc::channel();
let (pus_event_tx, pus_event_rx) = mpsc::channel(); let (pus_event_tx, pus_event_rx) = mpsc::channel();
@ -106,39 +107,39 @@ fn static_tmtc_pool_main() {
mode_tc_sender: pus_mode_tx, mode_tc_sender: pus_mode_tx,
}; };
let pus_test_service = create_test_service_static( let pus_test_service = create_test_service_static(
tm_funnel_tx_sender.clone(), tm_sink_tx_sender.clone(),
shared_tc_pool.clone(), shared_tc_pool.clone(),
event_handler.clone_event_sender(), event_tx.clone(),
pus_test_rx, pus_test_rx,
); );
let pus_scheduler_service = create_scheduler_service_static( let pus_scheduler_service = create_scheduler_service_static(
tm_funnel_tx_sender.clone(), tm_sink_tx_sender.clone(),
tc_source.clone(), tc_source.clone(),
pus_sched_rx, pus_sched_rx,
create_sched_tc_pool(), create_sched_tc_pool(),
); );
let pus_event_service = create_event_service_static( let pus_event_service = create_event_service_static(
tm_funnel_tx_sender.clone(), tm_sink_tx_sender.clone(),
shared_tc_pool.clone(), shared_tc_pool.clone(),
pus_event_rx, pus_event_rx,
event_request_tx, event_request_tx,
); );
let pus_action_service = create_action_service_static( let pus_action_service = create_action_service_static(
tm_funnel_tx_sender.clone(), tm_sink_tx_sender.clone(),
shared_tc_pool.clone(), shared_tc_pool.clone(),
pus_action_rx, pus_action_rx,
request_map.clone(), request_map.clone(),
pus_action_reply_rx, pus_action_reply_rx,
); );
let pus_hk_service = create_hk_service_static( let pus_hk_service = create_hk_service_static(
tm_funnel_tx_sender.clone(), tm_sink_tx_sender.clone(),
shared_tc_pool.clone(), shared_tc_pool.clone(),
pus_hk_rx, pus_hk_rx,
request_map.clone(), request_map.clone(),
pus_hk_reply_rx, pus_hk_reply_rx,
); );
let pus_mode_service = create_mode_service_static( let pus_mode_service = create_mode_service_static(
tm_funnel_tx_sender.clone(), tm_sink_tx_sender.clone(),
shared_tc_pool.clone(), shared_tc_pool.clone(),
pus_mode_rx, pus_mode_rx,
request_map, request_map,
@ -156,7 +157,7 @@ fn static_tmtc_pool_main() {
let mut tmtc_task = TcSourceTaskStatic::new( let mut tmtc_task = TcSourceTaskStatic::new(
shared_tc_pool_wrapper.clone(), shared_tc_pool_wrapper.clone(),
tc_source_rx, tc_source_rx,
PusTcDistributor::new(tm_funnel_tx_sender, pus_router), PusTcDistributor::new(tm_sink_tx_sender, pus_router),
); );
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
@ -186,10 +187,10 @@ fn static_tmtc_pool_main() {
) )
.expect("tcp server creation failed"); .expect("tcp server creation failed");
let mut tm_funnel = TmFunnelStatic::new( let mut tm_sink = TmSinkStatic::new(
shared_tm_pool_wrapper, shared_tm_pool_wrapper,
sync_tm_tcp_source, sync_tm_tcp_source,
tm_funnel_rx, tm_sink_rx,
tm_server_tx, tm_server_tx,
); );
@ -209,7 +210,7 @@ fn static_tmtc_pool_main() {
mode_leaf_interface, mode_leaf_interface,
mgm_handler_composite_rx, mgm_handler_composite_rx,
pus_hk_reply_tx, pus_hk_reply_tx,
tm_funnel_tx, tm_sink_tx,
dummy_spi_interface, dummy_spi_interface,
shared_mgm_set, shared_mgm_set,
); );
@ -240,9 +241,9 @@ fn static_tmtc_pool_main() {
info!("Starting TM funnel task"); info!("Starting TM funnel task");
let jh_tm_funnel = thread::Builder::new() let jh_tm_funnel = thread::Builder::new()
.name("TM Funnel".to_string()) .name("tm sink".to_string())
.spawn(move || loop { .spawn(move || loop {
tm_funnel.operation(); tm_sink.operation();
}) })
.unwrap(); .unwrap();
@ -314,10 +315,11 @@ fn dyn_tmtc_pool_main() {
// Create event handling components // Create event handling components
// These sender handles are used to send event requests, for example to enable or disable // These sender handles are used to send event requests, for example to enable or disable
// certain events. // certain events.
let (event_tx, event_rx) = mpsc::sync_channel(100);
let (event_request_tx, event_request_rx) = mpsc::channel::<EventRequestWithToken>(); let (event_request_tx, event_request_rx) = mpsc::channel::<EventRequestWithToken>();
// The event task is the core handler to perform the event routing and TM handling as specified // The event task is the core handler to perform the event routing and TM handling as specified
// in the sat-rs documentation. // in the sat-rs documentation.
let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_request_rx); let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_rx, event_request_rx);
let (pus_test_tx, pus_test_rx) = mpsc::channel(); let (pus_test_tx, pus_test_rx) = mpsc::channel();
let (pus_event_tx, pus_event_rx) = mpsc::channel(); let (pus_event_tx, pus_event_rx) = mpsc::channel();
@ -339,11 +341,8 @@ fn dyn_tmtc_pool_main() {
mode_tc_sender: pus_mode_tx, mode_tc_sender: pus_mode_tx,
}; };
let pus_test_service = create_test_service_dynamic( let pus_test_service =
tm_funnel_tx.clone(), create_test_service_dynamic(tm_funnel_tx.clone(), event_tx.clone(), pus_test_rx);
event_handler.clone_event_sender(),
pus_test_rx,
);
let pus_scheduler_service = create_scheduler_service_dynamic( let pus_scheduler_service = create_scheduler_service_dynamic(
tm_funnel_tx.clone(), tm_funnel_tx.clone(),
tc_source_tx.clone(), tc_source_tx.clone(),
@ -411,7 +410,7 @@ fn dyn_tmtc_pool_main() {
) )
.expect("tcp server creation failed"); .expect("tcp server creation failed");
let mut tm_funnel = TmFunnelDynamic::new(sync_tm_tcp_source, tm_funnel_rx, tm_server_tx); let mut tm_funnel = TmSinkDynamic::new(sync_tm_tcp_source, tm_funnel_rx, tm_server_tx);
let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) = let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) =
mpsc::channel(); mpsc::channel();
@ -459,7 +458,7 @@ fn dyn_tmtc_pool_main() {
info!("Starting TM funnel task"); info!("Starting TM funnel task");
let jh_tm_funnel = thread::Builder::new() let jh_tm_funnel = thread::Builder::new()
.name("sat-rs tm-funnel".to_string()) .name("sat-rs tm-sink".to_string())
.spawn(move || loop { .spawn(move || loop {
tm_funnel.operation(); tm_funnel.operation();
}) })

View File

@ -1,23 +1,23 @@
use log::{error, warn}; use log::warn;
use satrs::action::{ActionRequest, ActionRequestVariant}; use satrs::action::{ActionRequest, ActionRequestVariant};
use satrs::params::WritableToBeBytes;
use satrs::pool::SharedStaticMemoryPool; use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::action::{ use satrs::pus::action::{
ActionReplyPus, ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap, ActionReplyPus, ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap,
}; };
use satrs::pus::verification::{ use satrs::pus::verification::{
FailParams, FailParamsWithStep, TcStateAccepted, TcStateStarted, VerificationReporter, handle_completion_failure_with_generic_params, handle_step_failure_with_generic_params,
FailParamHelper, FailParams, TcStateAccepted, TcStateStarted, VerificationReporter,
VerificationReportingProvider, VerificationToken, VerificationReportingProvider, VerificationToken,
}; };
use satrs::pus::{ use satrs::pus::{
ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
EcssTcInVecConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver, EcssTcInVecConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver,
MpscTmAsVecSender, PusPacketHandlerResult, PusReplyHandler, PusServiceHelper, MpscTmAsVecSender, PusPacketHandlingError, PusReplyHandler, PusServiceHelper,
PusTcToRequestConverter, PusTcToRequestConverter,
}; };
use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::request::{GenericMessage, UniqueApidTargetId};
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket}; use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId};
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::components::PUS_ACTION_SERVICE; use satrs_example::config::components::PUS_ACTION_SERVICE;
use satrs_example::config::tmtc_err; use satrs_example::config::tmtc_err;
@ -61,7 +61,7 @@ impl PusReplyHandler<ActivePusActionRequestStd, ActionReplyPus> for ActionReplyH
active_request: &ActivePusActionRequestStd, active_request: &ActivePusActionRequestStd,
tm_sender: &(impl EcssTmSender + ?Sized), tm_sender: &(impl EcssTmSender + ?Sized),
verification_handler: &impl VerificationReportingProvider, verification_handler: &impl VerificationReportingProvider,
time_stamp: &[u8], timestamp: &[u8],
) -> Result<bool, Self::Error> { ) -> Result<bool, Self::Error> {
let verif_token: VerificationToken<TcStateStarted> = active_request let verif_token: VerificationToken<TcStateStarted> = active_request
.token() .token()
@ -69,15 +69,23 @@ impl PusReplyHandler<ActivePusActionRequestStd, ActionReplyPus> for ActionReplyH
.expect("invalid token state"); .expect("invalid token state");
let remove_entry = match &reply.message.variant { let remove_entry = match &reply.message.variant {
ActionReplyVariant::CompletionFailed { error_code, params } => { ActionReplyVariant::CompletionFailed { error_code, params } => {
let mut fail_data_len = 0; let error_propagated = handle_completion_failure_with_generic_params(
if let Some(params) = params {
fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?;
}
verification_handler.completion_failure(
tm_sender, tm_sender,
verif_token, verif_token,
FailParams::new(time_stamp, error_code, &self.fail_data_buf[..fail_data_len]), verification_handler,
FailParamHelper {
error_code,
params: params.as_ref(),
timestamp,
small_data_buf: &mut self.fail_data_buf,
},
)?; )?;
if !error_propagated {
log::warn!(
"error params for completion failure were not propated: {:?}",
params.as_ref()
);
}
true true
} }
ActionReplyVariant::StepFailed { ActionReplyVariant::StepFailed {
@ -85,31 +93,35 @@ impl PusReplyHandler<ActivePusActionRequestStd, ActionReplyPus> for ActionReplyH
step, step,
params, params,
} => { } => {
let mut fail_data_len = 0; let error_propagated = handle_step_failure_with_generic_params(
if let Some(params) = params {
fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?;
}
verification_handler.step_failure(
tm_sender, tm_sender,
verif_token, verif_token,
FailParamsWithStep::new( verification_handler,
time_stamp, FailParamHelper {
&EcssEnumU16::new(*step),
error_code, error_code,
&self.fail_data_buf[..fail_data_len], params: params.as_ref(),
), timestamp,
small_data_buf: &mut self.fail_data_buf,
},
&EcssEnumU16::new(*step),
)?; )?;
if !error_propagated {
log::warn!(
"error params for completion failure were not propated: {:?}",
params.as_ref()
);
}
true true
} }
ActionReplyVariant::Completed => { ActionReplyVariant::Completed => {
verification_handler.completion_success(tm_sender, verif_token, time_stamp)?; verification_handler.completion_success(tm_sender, verif_token, timestamp)?;
true true
} }
ActionReplyVariant::StepSuccess { step } => { ActionReplyVariant::StepSuccess { step } => {
verification_handler.step_success( verification_handler.step_success(
tm_sender, tm_sender,
&verif_token, &verif_token,
time_stamp, timestamp,
EcssEnumU16::new(*step), EcssEnumU16::new(*step),
)?; )?;
false false
@ -266,43 +278,23 @@ pub struct ActionServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTc
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
for ActionServiceWrapper<TmSender, TcInMemConverter> for ActionServiceWrapper<TmSender, TcInMemConverter>
{ {
/// Returns [true] if the packet handling is finished. const SERVICE_ID: u8 = PusServiceId::Action as u8;
fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { const SERVICE_STR: &'static str = "action";
match self.service.poll_and_handle_next_tc(time_stamp) {
Ok(result) => match result { delegate::delegate! {
PusPacketHandlerResult::RequestHandled => {} to self.service {
PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { fn poll_and_handle_next_tc(
warn!("PUS 8 partial packet handling success: {e:?}") &mut self,
} time_stamp: &[u8],
PusPacketHandlerResult::CustomSubservice(invalid, _) => { ) -> Result<HandlingStatus, PusPacketHandlingError>;
warn!("PUS 8 invalid subservice {invalid}");
} fn poll_and_handle_next_reply(
PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { &mut self,
warn!("PUS 8 subservice {subservice} not implemented"); time_stamp: &[u8],
} ) -> Result<HandlingStatus, EcssTmtcError>;
PusPacketHandlerResult::Empty => return HandlingStatus::Empty,
}, fn check_for_request_timeouts(&mut self);
Err(error) => {
error!("PUS packet handling error: {error:?}");
// To avoid permanent loops on error cases.
return HandlingStatus::Empty;
}
} }
HandlingStatus::HandledOne
}
fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus {
// This only fails if all senders disconnected. Treat it like an empty queue.
self.service
.poll_and_check_next_reply(time_stamp)
.unwrap_or_else(|e| {
warn!("PUS 8: Handling reply failed with error {e:?}");
HandlingStatus::Empty
})
}
fn check_for_request_timeouts(&mut self) {
self.service.check_for_request_timeouts();
} }
} }
@ -417,7 +409,7 @@ mod tests {
} }
let result = result.unwrap(); let result = result.unwrap();
match result { match result {
PusPacketHandlerResult::RequestHandled => (), HandlingStatus::HandledOne => (),
_ => panic!("unexpected result {result:?}"), _ => panic!("unexpected result {result:?}"),
} }
} }
@ -429,19 +421,19 @@ mod tests {
} }
let result = result.unwrap(); let result = result.unwrap();
match result { match result {
PusPacketHandlerResult::Empty => (), HandlingStatus::Empty => (),
_ => panic!("unexpected result {result:?}"), _ => panic!("unexpected result {result:?}"),
} }
} }
pub fn verify_next_reply_is_handled_properly(&mut self, time_stamp: &[u8]) { pub fn verify_next_reply_is_handled_properly(&mut self, time_stamp: &[u8]) {
let result = self.service.poll_and_check_next_reply(time_stamp); let result = self.service.poll_and_handle_next_reply(time_stamp);
assert!(result.is_ok()); assert!(result.is_ok());
assert_eq!(result.unwrap(), HandlingStatus::HandledOne); assert_eq!(result.unwrap(), HandlingStatus::HandledOne);
} }
pub fn verify_all_replies_handled(&mut self, time_stamp: &[u8]) { pub fn verify_all_replies_handled(&mut self, time_stamp: &[u8]) {
let result = self.service.poll_and_check_next_reply(time_stamp); let result = self.service.poll_and_handle_next_reply(time_stamp);
assert!(result.is_ok()); assert!(result.is_ok());
assert_eq!(result.unwrap(), HandlingStatus::Empty); assert_eq!(result.unwrap(), HandlingStatus::Empty);
} }

View File

@ -1,19 +1,20 @@
use std::sync::mpsc; use std::sync::mpsc;
use crate::pus::create_verification_reporter; use crate::pus::create_verification_reporter;
use log::{error, warn};
use satrs::pool::SharedStaticMemoryPool; use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::event_man::EventRequestWithToken; use satrs::pus::event_man::EventRequestWithToken;
use satrs::pus::event_srv::PusEventServiceHandler; use satrs::pus::event_srv::PusEventServiceHandler;
use satrs::pus::verification::VerificationReporter; use satrs::pus::verification::VerificationReporter;
use satrs::pus::{ use satrs::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter,
EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlerResult, PusServiceHelper, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver,
MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper,
}; };
use satrs::spacepackets::ecss::PusServiceId;
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::components::PUS_EVENT_MANAGEMENT; use satrs_example::config::components::PUS_EVENT_MANAGEMENT;
use super::HandlingStatus; use super::{DirectPusService, HandlingStatus};
pub fn create_event_service_static( pub fn create_event_service_static(
tm_sender: PacketSenderWithSharedPool, tm_sender: PacketSenderWithSharedPool,
@ -61,26 +62,52 @@ pub struct EventServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcI
PusEventServiceHandler<MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter>, PusEventServiceHandler<MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
} }
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> DirectPusService
EventServiceWrapper<TmSender, TcInMemConverter> for EventServiceWrapper<TmSender, TcInMemConverter>
{ {
pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { const SERVICE_ID: u8 = PusServiceId::Event as u8;
match self.handler.poll_and_handle_next_tc(time_stamp) {
Ok(result) => match result { const SERVICE_STR: &'static str = "events";
PusPacketHandlerResult::RequestHandled => {}
PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus {
warn!("PUS 5 partial packet handling success: {e:?}") let error_handler = |partial_error: &PartialPusHandlingError| {
} log::warn!(
PusPacketHandlerResult::CustomSubservice(invalid, _) => { "PUS {}({}) partial error: {:?}",
warn!("PUS 5 invalid subservice {invalid}"); Self::SERVICE_ID,
} Self::SERVICE_STR,
PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { partial_error
warn!("PUS 5 subservice {subservice} not implemented"); );
} };
PusPacketHandlerResult::Empty => return HandlingStatus::Empty, let result = self
}, .handler
Err(error) => { .poll_and_handle_next_tc(error_handler, time_stamp);
error!("PUS packet handling error: {error:?}") if let Err(e) = result {
log::warn!(
"PUS {}({}) error: {:?}",
Self::SERVICE_ID,
Self::SERVICE_STR,
e
);
// To avoid permanent loops on continuous errors.
return HandlingStatus::Empty;
}
match result.unwrap() {
DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status,
DirectPusPacketHandlerResult::CustomSubservice(subservice, _) => {
log::warn!(
"PUS {}({}) subservice {} not implemented",
Self::SERVICE_ID,
Self::SERVICE_STR,
subservice
);
}
DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => {
log::warn!(
"PUS {}({}) subservice {} not implemented",
Self::SERVICE_ID,
Self::SERVICE_STR,
subservice
);
} }
} }
HandlingStatus::HandledOne HandlingStatus::HandledOne

View File

@ -1,5 +1,4 @@
use derive_new::new; use derive_new::new;
use log::{error, warn};
use satrs::hk::{CollectionIntervalFactor, HkRequest, HkRequestVariant, UniqueId}; use satrs::hk::{CollectionIntervalFactor, HkRequest, HkRequestVariant, UniqueId};
use satrs::pool::SharedStaticMemoryPool; use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::verification::{ use satrs::pus::verification::{
@ -10,11 +9,11 @@ use satrs::pus::{
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken, ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken,
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender,
EcssTmtcError, GenericConversionError, MpscTcReceiver, MpscTmAsVecSender, EcssTmtcError, GenericConversionError, MpscTcReceiver, MpscTmAsVecSender,
PusPacketHandlerResult, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter, PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter,
}; };
use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::request::{GenericMessage, UniqueApidTargetId};
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::{hk, PusPacket}; use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId};
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::components::PUS_HK_SERVICE; use satrs_example::config::components::PUS_HK_SERVICE;
use satrs_example::config::{hk_err, tmtc_err}; use satrs_example::config::{hk_err, tmtc_err};
@ -24,7 +23,7 @@ use std::time::Duration;
use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler}; use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler};
use crate::requests::GenericRequestRouter; use crate::requests::GenericRequestRouter;
use super::{HandlingStatus, PusTargetedRequestService}; use super::{HandlingStatus, PusTargetedRequestService, TargetedPusService};
#[derive(Clone, PartialEq, Debug, new)] #[derive(Clone, PartialEq, Debug, new)]
pub struct HkReply { pub struct HkReply {
@ -297,45 +296,26 @@ pub struct HkServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMe
>, >,
} }
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
HkServiceWrapper<TmSender, TcInMemConverter> for HkServiceWrapper<TmSender, TcInMemConverter>
{ {
pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { const SERVICE_ID: u8 = PusServiceId::Housekeeping as u8;
match self.service.poll_and_handle_next_tc(time_stamp) { const SERVICE_STR: &'static str = "housekeeping";
Ok(result) => match result {
PusPacketHandlerResult::RequestHandled => {} delegate::delegate! {
PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { to self.service {
warn!("PUS 3 partial packet handling success: {e:?}") fn poll_and_handle_next_tc(
} &mut self,
PusPacketHandlerResult::CustomSubservice(invalid, _) => { time_stamp: &[u8],
warn!("PUS 3 invalid subservice {invalid}"); ) -> Result<HandlingStatus, PusPacketHandlingError>;
}
PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { fn poll_and_handle_next_reply(
warn!("PUS 3 subservice {subservice} not implemented"); &mut self,
} time_stamp: &[u8],
PusPacketHandlerResult::Empty => return HandlingStatus::Empty, ) -> Result<HandlingStatus, EcssTmtcError>;
},
Err(error) => { fn check_for_request_timeouts(&mut self);
error!("PUS packet handling error: {error:?}");
// To avoid permanent loops on error cases.
return HandlingStatus::Empty;
}
} }
HandlingStatus::HandledOne
}
pub fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus {
// This only fails if all senders disconnected. Treat it like an empty queue.
self.service
.poll_and_check_next_reply(time_stamp)
.unwrap_or_else(|e| {
warn!("PUS 3: Handling reply failed with error {e:?}");
HandlingStatus::Empty
})
}
pub fn check_for_request_timeouts(&mut self) {
self.service.check_for_request_timeouts();
} }
} }

View File

@ -8,8 +8,8 @@ use satrs::pus::verification::{
use satrs::pus::{ use satrs::pus::{
ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter,
EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError, GenericRoutingError, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError, GenericRoutingError,
PusPacketHandlerResult, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, PusServiceHelper,
PusServiceHelper, PusTcToRequestConverter, TcInMemory, PusTcToRequestConverter, TcInMemory,
}; };
use satrs::queue::{GenericReceiveError, GenericSendError}; use satrs::queue::{GenericReceiveError, GenericSendError};
use satrs::request::{Apid, GenericMessage, MessageMetadata}; use satrs::request::{Apid, GenericMessage, MessageMetadata};
@ -31,12 +31,6 @@ pub mod scheduler;
pub mod stack; pub mod stack;
pub mod test; pub mod test;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum HandlingStatus {
Empty,
HandledOne,
}
pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter { pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter {
let verif_cfg = VerificationReporterCfg::new(apid, 1, 2, 8).unwrap(); let verif_cfg = VerificationReporterCfg::new(apid, 1, 2, 8).unwrap();
// Every software component which needs to generate verification telemetry, gets a cloned // Every software component which needs to generate verification telemetry, gets a cloned
@ -79,7 +73,7 @@ impl<TmSender: EcssTmSender> PusTcDistributor<TmSender> {
pub fn handle_tc_packet_vec( pub fn handle_tc_packet_vec(
&mut self, &mut self,
packet_as_vec: PacketAsVec, packet_as_vec: PacketAsVec,
) -> Result<PusPacketHandlerResult, GenericSendError> { ) -> Result<HandlingStatus, GenericSendError> {
self.handle_tc_generic(packet_as_vec.sender_id, None, &packet_as_vec.packet) self.handle_tc_generic(packet_as_vec.sender_id, None, &packet_as_vec.packet)
} }
@ -87,7 +81,7 @@ impl<TmSender: EcssTmSender> PusTcDistributor<TmSender> {
&mut self, &mut self,
packet_in_pool: PacketInPool, packet_in_pool: PacketInPool,
pus_tc_copy: &[u8], pus_tc_copy: &[u8],
) -> Result<PusPacketHandlerResult, GenericSendError> { ) -> Result<HandlingStatus, GenericSendError> {
self.handle_tc_generic( self.handle_tc_generic(
packet_in_pool.sender_id, packet_in_pool.sender_id,
Some(packet_in_pool.store_addr), Some(packet_in_pool.store_addr),
@ -100,7 +94,7 @@ impl<TmSender: EcssTmSender> PusTcDistributor<TmSender> {
sender_id: ComponentId, sender_id: ComponentId,
addr_opt: Option<PoolAddr>, addr_opt: Option<PoolAddr>,
raw_tc: &[u8], raw_tc: &[u8],
) -> Result<PusPacketHandlerResult, GenericSendError> { ) -> Result<HandlingStatus, GenericSendError> {
let pus_tc_result = PusTcReader::new(raw_tc); let pus_tc_result = PusTcReader::new(raw_tc);
if pus_tc_result.is_err() { if pus_tc_result.is_err() {
log::warn!( log::warn!(
@ -109,7 +103,8 @@ impl<TmSender: EcssTmSender> PusTcDistributor<TmSender> {
pus_tc_result.unwrap_err() pus_tc_result.unwrap_err()
); );
log::warn!("raw data: {:x?}", raw_tc); log::warn!("raw data: {:x?}", raw_tc);
return Ok(PusPacketHandlerResult::RequestHandled); // TODO: Shouldn't this be an error?
return Ok(HandlingStatus::HandledOne);
} }
let pus_tc = pus_tc_result.unwrap().0; let pus_tc = pus_tc_result.unwrap().0;
let init_token = self.verif_reporter.add_tc(&pus_tc); let init_token = self.verif_reporter.add_tc(&pus_tc);
@ -189,17 +184,65 @@ impl<TmSender: EcssTmSender> PusTcDistributor<TmSender> {
} }
} }
} }
Ok(PusPacketHandlerResult::RequestHandled) Ok(HandlingStatus::HandledOne)
} }
} }
pub trait TargetedPusService { pub trait TargetedPusService {
/// Returns [true] if the packet handling is finished. const SERVICE_ID: u8;
fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus; const SERVICE_STR: &'static str;
fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus;
fn poll_and_handle_next_tc_default_handler(&mut self, time_stamp: &[u8]) -> HandlingStatus {
let result = self.poll_and_handle_next_tc(time_stamp);
if let Err(e) = result {
log::error!(
"PUS service {}({})packet handling error: {:?}",
Self::SERVICE_ID,
Self::SERVICE_STR,
e
);
// To avoid permanent loops on error cases.
return HandlingStatus::Empty;
}
result.unwrap()
}
fn poll_and_handle_next_reply_default_handler(&mut self, time_stamp: &[u8]) -> HandlingStatus {
// This only fails if all senders disconnected. Treat it like an empty queue.
self.poll_and_handle_next_reply(time_stamp)
.unwrap_or_else(|e| {
warn!(
"PUS servce {}({}): Handling reply failed with error {:?}",
Self::SERVICE_ID,
Self::SERVICE_STR,
e
);
HandlingStatus::Empty
})
}
fn poll_and_handle_next_tc(
&mut self,
time_stamp: &[u8],
) -> Result<HandlingStatus, PusPacketHandlingError>;
fn poll_and_handle_next_reply(
&mut self,
time_stamp: &[u8],
) -> Result<HandlingStatus, EcssTmtcError>;
fn check_for_request_timeouts(&mut self); fn check_for_request_timeouts(&mut self);
} }
/// Generic trait for services which handle packets directly. Kept minimal right now because
/// of the difficulty to allow flexible user code for these services..
pub trait DirectPusService {
const SERVICE_ID: u8;
const SERVICE_STR: &'static str;
fn poll_and_handle_next_tc(&mut self, timestamp: &[u8]) -> HandlingStatus;
}
/// This is a generic handler class for all PUS services where a PUS telecommand is converted /// This is a generic handler class for all PUS services where a PUS telecommand is converted
/// to a targeted request. /// to a targeted request.
/// ///
@ -297,10 +340,10 @@ where
pub fn poll_and_handle_next_tc( pub fn poll_and_handle_next_tc(
&mut self, &mut self,
time_stamp: &[u8], time_stamp: &[u8],
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { ) -> Result<HandlingStatus, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() { if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty); return Ok(HandlingStatus::Empty);
} }
let ecss_tc_and_token = possible_packet.unwrap(); let ecss_tc_and_token = possible_packet.unwrap();
self.service_helper self.service_helper
@ -356,7 +399,7 @@ where
return Err(e.into()); return Err(e.into());
} }
} }
Ok(PusPacketHandlerResult::RequestHandled) Ok(HandlingStatus::HandledOne)
} }
fn handle_conversion_to_request_error( fn handle_conversion_to_request_error(
@ -409,7 +452,7 @@ where
} }
} }
pub fn poll_and_check_next_reply( pub fn poll_and_handle_next_reply(
&mut self, &mut self,
time_stamp: &[u8], time_stamp: &[u8],
) -> Result<HandlingStatus, EcssTmtcError> { ) -> Result<HandlingStatus, EcssTmtcError> {
@ -439,20 +482,17 @@ where
return Ok(()); return Ok(());
} }
let active_request = active_req_opt.unwrap(); let active_request = active_req_opt.unwrap();
let request_finished = self let result = self.reply_handler.handle_reply(
.reply_handler reply,
.handle_reply( active_request,
reply, &self.service_helper.common.tm_sender,
active_request, &self.service_helper.common.verif_reporter,
&self.service_helper.common.tm_sender, time_stamp,
&self.service_helper.common.verif_reporter, );
time_stamp, if result.is_err() || (result.is_ok() && *result.as_ref().unwrap()) {
)
.unwrap_or(false);
if request_finished {
self.active_request_map.remove(reply.request_id()); self.active_request_map.remove(reply.request_id());
} }
Ok(()) result.map(|_| ())
} }
pub fn check_for_request_timeouts(&mut self) { pub fn check_for_request_timeouts(&mut self) {

View File

@ -1,5 +1,4 @@
use derive_new::new; use derive_new::new;
use log::{error, warn};
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use std::sync::mpsc; use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
@ -9,7 +8,7 @@ use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::verification::VerificationReporter; use satrs::pus::verification::VerificationReporter;
use satrs::pus::{ use satrs::pus::{
DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlerResult, EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlingError,
PusServiceHelper, PusServiceHelper,
}; };
use satrs::request::GenericMessage; use satrs::request::GenericMessage;
@ -36,7 +35,7 @@ use satrs::{
ComponentId, ComponentId,
}; };
use satrs_example::config::components::PUS_MODE_SERVICE; use satrs_example::config::components::PUS_MODE_SERVICE;
use satrs_example::config::{mode_err, tmtc_err}; use satrs_example::config::{mode_err, tmtc_err, CustomPusServiceId};
use super::{ use super::{
create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus, create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus,
@ -272,44 +271,26 @@ pub struct ModeServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: EcssTcIn
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> TargetedPusService
for ModeServiceWrapper<TmSender, TcInMemConverter> for ModeServiceWrapper<TmSender, TcInMemConverter>
{ {
/// Returns [true] if the packet handling is finished. const SERVICE_ID: u8 = CustomPusServiceId::Mode as u8;
fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { const SERVICE_STR: &'static str = "mode";
match self.service.poll_and_handle_next_tc(time_stamp) {
Ok(result) => match result { delegate::delegate! {
PusPacketHandlerResult::RequestHandled => {} to self.service {
PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { fn poll_and_handle_next_tc(
warn!("PUS mode service: partial packet handling success: {e:?}") &mut self,
} time_stamp: &[u8],
PusPacketHandlerResult::CustomSubservice(invalid, _) => { ) -> Result<HandlingStatus, PusPacketHandlingError>;
warn!("PUS mode service: invalid subservice {invalid}");
} fn poll_and_handle_next_reply(
PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { &mut self,
warn!("PUS mode service: {subservice} not implemented"); time_stamp: &[u8],
} ) -> Result<HandlingStatus, EcssTmtcError>;
PusPacketHandlerResult::Empty => return HandlingStatus::Empty,
}, fn check_for_request_timeouts(&mut self);
Err(error) => {
error!("PUS mode service: packet handling error: {error:?}");
// To avoid permanent loops on error cases.
return HandlingStatus::Empty;
}
} }
HandlingStatus::HandledOne
}
fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus {
self.service
.poll_and_check_next_reply(time_stamp)
.unwrap_or_else(|e| {
warn!("PUS action service: Handling reply failed with error {e:?}");
HandlingStatus::HandledOne
})
}
fn check_for_request_timeouts(&mut self) {
self.service.check_for_request_timeouts();
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0, TEST_UNIQUE_ID_0}; use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0, TEST_UNIQUE_ID_0};

View File

@ -2,20 +2,22 @@ use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use crate::pus::create_verification_reporter; use crate::pus::create_verification_reporter;
use log::{error, info, warn}; use log::info;
use satrs::pool::{PoolProvider, StaticMemoryPool}; use satrs::pool::{PoolProvider, StaticMemoryPool};
use satrs::pus::scheduler::{PusScheduler, TcInfo}; use satrs::pus::scheduler::{PusScheduler, TcInfo};
use satrs::pus::scheduler_srv::PusSchedServiceHandler; use satrs::pus::scheduler_srv::PusSchedServiceHandler;
use satrs::pus::verification::VerificationReporter; use satrs::pus::verification::VerificationReporter;
use satrs::pus::{ use satrs::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter,
EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlerResult, PusServiceHelper, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver,
MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper,
}; };
use satrs::spacepackets::ecss::PusServiceId;
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool}; use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool};
use satrs::ComponentId; use satrs::ComponentId;
use satrs_example::config::components::PUS_SCHED_SERVICE; use satrs_example::config::components::PUS_SCHED_SERVICE;
use super::HandlingStatus; use super::{DirectPusService, HandlingStatus};
pub trait TcReleaser { pub trait TcReleaser {
fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool; fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool;
@ -77,6 +79,61 @@ pub struct SchedulingServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: Ec
pub tc_releaser: Box<dyn TcReleaser + Send>, pub tc_releaser: Box<dyn TcReleaser + Send>,
} }
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> DirectPusService
for SchedulingServiceWrapper<TmSender, TcInMemConverter>
{
const SERVICE_ID: u8 = PusServiceId::Verification as u8;
const SERVICE_STR: &'static str = "verification";
fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus {
let error_handler = |partial_error: &PartialPusHandlingError| {
log::warn!(
"PUS {}({}) partial error: {:?}",
Self::SERVICE_ID,
Self::SERVICE_STR,
partial_error
);
};
let result = self.pus_11_handler.poll_and_handle_next_tc(
error_handler,
time_stamp,
&mut self.sched_tc_pool,
);
if let Err(e) = result {
log::warn!(
"PUS {}({}) error: {:?}",
Self::SERVICE_ID,
Self::SERVICE_STR,
e
);
// To avoid permanent loops on continuous errors.
return HandlingStatus::Empty;
}
match result.unwrap() {
DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status,
DirectPusPacketHandlerResult::CustomSubservice(subservice, _) => {
log::warn!(
"PUS {}({}) subservice {} not implemented",
Self::SERVICE_ID,
Self::SERVICE_STR,
subservice
);
}
DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => {
log::warn!(
"PUS {}({}) subservice {} not implemented",
Self::SERVICE_ID,
Self::SERVICE_STR,
subservice
);
}
}
HandlingStatus::HandledOne
}
}
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
SchedulingServiceWrapper<TmSender, TcInMemConverter> SchedulingServiceWrapper<TmSender, TcInMemConverter>
{ {
@ -103,31 +160,6 @@ impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
info!("{released_tcs} TC(s) released from scheduler"); info!("{released_tcs} TC(s) released from scheduler");
} }
} }
pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus {
match self
.pus_11_handler
.poll_and_handle_next_tc(time_stamp, &mut self.sched_tc_pool)
{
Ok(result) => match result {
PusPacketHandlerResult::RequestHandled => {}
PusPacketHandlerResult::RequestHandledPartialSuccess(e) => {
warn!("PUS11 partial packet handling success: {e:?}")
}
PusPacketHandlerResult::CustomSubservice(invalid, _) => {
warn!("PUS11 invalid subservice {invalid}");
}
PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => {
warn!("PUS11: Subservice {subservice} not implemented");
}
PusPacketHandlerResult::Empty => return HandlingStatus::Empty,
},
Err(error) => {
error!("PUS packet handling error: {error:?}")
}
}
HandlingStatus::HandledOne
}
} }
pub fn create_scheduler_service_static( pub fn create_scheduler_service_static(

View File

@ -7,10 +7,12 @@ use satrs::{
use super::{ use super::{
action::ActionServiceWrapper, event::EventServiceWrapper, hk::HkServiceWrapper, action::ActionServiceWrapper, event::EventServiceWrapper, hk::HkServiceWrapper,
scheduler::SchedulingServiceWrapper, test::TestCustomServiceWrapper, HandlingStatus, scheduler::SchedulingServiceWrapper, test::TestCustomServiceWrapper, DirectPusService,
TargetedPusService, HandlingStatus, TargetedPusService,
}; };
// TODO: For better extensibility, we could create 2 vectors: One for direct PUS services and one
// for targeted services..
#[derive(new)] #[derive(new)]
pub struct PusStack<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> { pub struct PusStack<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> {
test_srv: TestCustomServiceWrapper<TmSender, TcInMemConverter>, test_srv: TestCustomServiceWrapper<TmSender, TcInMemConverter>,
@ -28,52 +30,28 @@ impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
// Release all telecommands which reached their release time before calling the service // Release all telecommands which reached their release time before calling the service
// handlers. // handlers.
self.schedule_srv.release_tcs(); self.schedule_srv.release_tcs();
let time_stamp = cds::CdsTime::now_with_u16_days() let timestamp = cds::CdsTime::now_with_u16_days()
.expect("time stamp generation error") .expect("time stamp generation error")
.to_vec() .to_vec()
.unwrap(); .unwrap();
let mut loop_count = 0_u32;
// Hot loop which will run continuously until all request and reply handling is done.
loop { loop {
let mut nothing_to_do = true; let mut nothing_to_do = true;
let mut is_srv_finished = Self::direct_service_checker(&mut self.test_srv, &timestamp, &mut nothing_to_do);
|_srv_id: u8, Self::direct_service_checker(&mut self.schedule_srv, &timestamp, &mut nothing_to_do);
tc_handling_status: HandlingStatus, Self::direct_service_checker(&mut self.event_srv, &timestamp, &mut nothing_to_do);
reply_handling_status: Option<HandlingStatus>| { Self::targeted_service_checker(
if tc_handling_status == HandlingStatus::HandledOne &mut self.action_srv_wrapper,
|| (reply_handling_status.is_some() &timestamp,
&& reply_handling_status.unwrap() == HandlingStatus::HandledOne) &mut nothing_to_do,
{
nothing_to_do = false;
}
};
is_srv_finished(
17,
self.test_srv.poll_and_handle_next_packet(&time_stamp),
None,
); );
is_srv_finished( Self::targeted_service_checker(
11, &mut self.hk_srv_wrapper,
self.schedule_srv.poll_and_handle_next_tc(&time_stamp), &timestamp,
None, &mut nothing_to_do,
);
is_srv_finished(5, self.event_srv.poll_and_handle_next_tc(&time_stamp), None);
is_srv_finished(
8,
self.action_srv_wrapper.poll_and_handle_next_tc(&time_stamp),
Some(
self.action_srv_wrapper
.poll_and_handle_next_reply(&time_stamp),
),
);
is_srv_finished(
3,
self.hk_srv_wrapper.poll_and_handle_next_tc(&time_stamp),
Some(self.hk_srv_wrapper.poll_and_handle_next_reply(&time_stamp)),
);
is_srv_finished(
200,
self.mode_srv.poll_and_handle_next_tc(&time_stamp),
Some(self.mode_srv.poll_and_handle_next_reply(&time_stamp)),
); );
Self::targeted_service_checker(&mut self.mode_srv, &timestamp, &mut nothing_to_do);
if nothing_to_do { if nothing_to_do {
// Timeout checking is only done once. // Timeout checking is only done once.
self.action_srv_wrapper.check_for_request_timeouts(); self.action_srv_wrapper.check_for_request_timeouts();
@ -81,6 +59,37 @@ impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
self.mode_srv.check_for_request_timeouts(); self.mode_srv.check_for_request_timeouts();
break; break;
} }
// Safety mechanism to avoid infinite loops.
loop_count += 1;
if loop_count >= 500 {
log::warn!("reached PUS stack loop count 500, breaking");
break;
}
}
}
pub fn direct_service_checker<S: DirectPusService>(
service: &mut S,
timestamp: &[u8],
nothing_to_do: &mut bool,
) {
let handling_status = service.poll_and_handle_next_tc(timestamp);
if handling_status == HandlingStatus::HandledOne {
*nothing_to_do = false;
}
}
pub fn targeted_service_checker<S: TargetedPusService>(
service: &mut S,
timestamp: &[u8],
nothing_to_do: &mut bool,
) {
let request_handling = service.poll_and_handle_next_tc_default_handler(timestamp);
let reply_handling = service.poll_and_handle_next_reply_default_handler(timestamp);
if request_handling == HandlingStatus::HandledOne
|| reply_handling == HandlingStatus::HandledOne
{
*nothing_to_do = false;
} }
} }
} }

View File

@ -1,29 +1,27 @@
use crate::pus::create_verification_reporter; use crate::pus::create_verification_reporter;
use log::{info, warn}; use log::info;
use satrs::event_man::{EventMessage, EventMessageU32}; use satrs::event_man::{EventMessage, EventMessageU32};
use satrs::pool::SharedStaticMemoryPool; use satrs::pool::SharedStaticMemoryPool;
use satrs::pus::test::PusService17TestHandler; use satrs::pus::test::PusService17TestHandler;
use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider}; use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider};
use satrs::pus::EcssTcInSharedStoreConverter;
use satrs::pus::{ use satrs::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver, DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter,
MpscTmAsVecSender, PusPacketHandlerResult, PusServiceHelper, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PusServiceHelper,
}; };
use satrs::pus::{EcssTcInSharedStoreConverter, PartialPusHandlingError};
use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::PusPacket; use satrs::spacepackets::ecss::{PusPacket, PusServiceId};
use satrs::spacepackets::time::cds::CdsTime;
use satrs::spacepackets::time::TimeWriter;
use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use satrs_example::config::components::PUS_TEST_SERVICE; use satrs_example::config::components::PUS_TEST_SERVICE;
use satrs_example::config::{tmtc_err, TEST_EVENT}; use satrs_example::config::{tmtc_err, TEST_EVENT};
use std::sync::mpsc; use std::sync::mpsc;
use super::HandlingStatus; use super::{DirectPusService, HandlingStatus};
pub fn create_test_service_static( pub fn create_test_service_static(
tm_sender: PacketSenderWithSharedPool, tm_sender: PacketSenderWithSharedPool,
tc_pool: SharedStaticMemoryPool, tc_pool: SharedStaticMemoryPool,
event_sender: mpsc::Sender<EventMessageU32>, event_sender: mpsc::SyncSender<EventMessageU32>,
pus_test_rx: mpsc::Receiver<EcssTcAndToken>, pus_test_rx: mpsc::Receiver<EcssTcAndToken>,
) -> TestCustomServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> { ) -> TestCustomServiceWrapper<PacketSenderWithSharedPool, EcssTcInSharedStoreConverter> {
let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new( let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new(
@ -35,13 +33,13 @@ pub fn create_test_service_static(
)); ));
TestCustomServiceWrapper { TestCustomServiceWrapper {
handler: pus17_handler, handler: pus17_handler,
test_srv_event_sender: event_sender, event_tx: event_sender,
} }
} }
pub fn create_test_service_dynamic( pub fn create_test_service_dynamic(
tm_funnel_tx: mpsc::Sender<PacketAsVec>, tm_funnel_tx: mpsc::Sender<PacketAsVec>,
event_sender: mpsc::Sender<EventMessageU32>, event_sender: mpsc::SyncSender<EventMessageU32>,
pus_test_rx: mpsc::Receiver<EcssTcAndToken>, pus_test_rx: mpsc::Receiver<EcssTcAndToken>,
) -> TestCustomServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> { ) -> TestCustomServiceWrapper<MpscTmAsVecSender, EcssTcInVecConverter> {
let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new( let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new(
@ -53,7 +51,7 @@ pub fn create_test_service_dynamic(
)); ));
TestCustomServiceWrapper { TestCustomServiceWrapper {
handler: pus17_handler, handler: pus17_handler,
test_srv_event_sender: event_sender, event_tx: event_sender,
} }
} }
@ -61,33 +59,55 @@ pub struct TestCustomServiceWrapper<TmSender: EcssTmSender, TcInMemConverter: Ec
{ {
pub handler: pub handler:
PusService17TestHandler<MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter>, PusService17TestHandler<MpscTcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub test_srv_event_sender: mpsc::Sender<EventMessageU32>, pub event_tx: mpsc::SyncSender<EventMessageU32>,
} }
impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter> DirectPusService
TestCustomServiceWrapper<TmSender, TcInMemConverter> for TestCustomServiceWrapper<TmSender, TcInMemConverter>
{ {
pub fn poll_and_handle_next_packet(&mut self, time_stamp: &[u8]) -> HandlingStatus { const SERVICE_ID: u8 = PusServiceId::Test as u8;
let res = self.handler.poll_and_handle_next_tc(time_stamp);
if res.is_err() { const SERVICE_STR: &'static str = "test";
warn!("PUS17 handler failed with error {:?}", res.unwrap_err());
return HandlingStatus::HandledOne; fn poll_and_handle_next_tc(&mut self, timestamp: &[u8]) -> HandlingStatus {
let error_handler = |partial_error: &PartialPusHandlingError| {
log::warn!(
"PUS {}({}) partial error: {:?}",
Self::SERVICE_ID,
Self::SERVICE_STR,
partial_error
);
};
let res = self
.handler
.poll_and_handle_next_tc(error_handler, timestamp);
if let Err(e) = res {
log::warn!(
"PUS {}({}) error: {:?}",
Self::SERVICE_ID,
Self::SERVICE_STR,
e
);
// To avoid permanent loops on continuous errors.
return HandlingStatus::Empty;
} }
match res.unwrap() { match res.unwrap() {
PusPacketHandlerResult::RequestHandled => { DirectPusPacketHandlerResult::Handled(handling_status) => {
info!("Received PUS ping command TC[17,1]"); if handling_status == HandlingStatus::HandledOne {
info!("Sent ping reply PUS TM[17,2]"); info!("Received PUS ping command TC[17,1]");
info!("Sent ping reply PUS TM[17,2]");
}
return handling_status;
} }
PusPacketHandlerResult::RequestHandledPartialSuccess(partial_err) => { DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => {
warn!( log::warn!(
"Handled PUS ping command with partial success: {:?}", "PUS {}({}) subservice {} not implemented",
partial_err Self::SERVICE_ID,
Self::SERVICE_STR,
subservice
); );
} }
PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { DirectPusPacketHandlerResult::CustomSubservice(subservice, token) => {
warn!("PUS17: Subservice {subservice} not implemented")
}
PusPacketHandlerResult::CustomSubservice(subservice, token) => {
let (tc, _) = PusTcReader::new( let (tc, _) = PusTcReader::new(
self.handler self.handler
.service_helper .service_helper
@ -95,29 +115,34 @@ impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
.tc_slice_raw(), .tc_slice_raw(),
) )
.unwrap(); .unwrap();
let time_stamper = CdsTime::now_with_u16_days().unwrap();
let mut stamp_buf: [u8; 7] = [0; 7];
time_stamper.write_to_bytes(&mut stamp_buf).unwrap();
if subservice == 128 { if subservice == 128 {
info!("Generating test event"); info!("generating test event");
self.test_srv_event_sender self.event_tx
.send(EventMessage::new(PUS_TEST_SERVICE.id(), TEST_EVENT.into())) .send(EventMessage::new(PUS_TEST_SERVICE.id(), TEST_EVENT.into()))
.expect("Sending test event failed"); .expect("Sending test event failed");
let start_token = self match self.handler.service_helper.verif_reporter().start_success(
.handler self.handler.service_helper.tm_sender(),
.service_helper token,
.verif_reporter() timestamp,
.start_success(self.handler.service_helper.tm_sender(), token, &stamp_buf) ) {
.expect("Error sending start success"); Ok(started_token) => {
self.handler if let Err(e) = self
.service_helper .handler
.verif_reporter() .service_helper
.completion_success( .verif_reporter()
self.handler.service_helper.tm_sender(), .completion_success(
start_token, self.handler.service_helper.tm_sender(),
&stamp_buf, started_token,
) timestamp,
.expect("Error sending completion success"); )
{
error_handler(&PartialPusHandlingError::Verification(e));
}
}
Err(e) => {
error_handler(&PartialPusHandlingError::Verification(e));
}
}
} else { } else {
let fail_data = [tc.subservice()]; let fail_data = [tc.subservice()];
self.handler self.handler
@ -127,7 +152,7 @@ impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
self.handler.service_helper.tm_sender(), self.handler.service_helper.tm_sender(),
token, token,
FailParams::new( FailParams::new(
&stamp_buf, timestamp,
&tmtc_err::INVALID_PUS_SUBSERVICE, &tmtc_err::INVALID_PUS_SUBSERVICE,
&fail_data, &fail_data,
), ),
@ -135,7 +160,6 @@ impl<TmSender: EcssTmSender, TcInMemConverter: EcssTcInMemConverter>
.expect("Sending start failure verification failed"); .expect("Sending start failure verification failed");
} }
} }
PusPacketHandlerResult::Empty => return HandlingStatus::Empty,
} }
HandlingStatus::HandledOne HandlingStatus::HandledOne
} }

View File

@ -1,12 +1,13 @@
use satrs::{ use satrs::{
pool::PoolProvider, pool::PoolProvider,
pus::HandlingStatus,
tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool}, tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool},
}; };
use std::sync::mpsc::{self, TryRecvError}; use std::sync::mpsc::{self, TryRecvError};
use satrs::pus::MpscTmAsVecSender; use satrs::pus::MpscTmAsVecSender;
use crate::pus::{HandlingStatus, PusTcDistributor}; use crate::pus::PusTcDistributor;
// TC source components where static pools are the backing memory of the received telecommands. // TC source components where static pools are the backing memory of the received telecommands.
pub struct TcSourceTaskStatic { pub struct TcSourceTaskStatic {

View File

@ -70,18 +70,23 @@ impl TmFunnelCommon {
} }
fn packet_printout(tm: &PusTmZeroCopyWriter) { fn packet_printout(tm: &PusTmZeroCopyWriter) {
info!("Sending PUS TM[{},{}]", tm.service(), tm.subservice()); info!(
"Sending PUS TM[{},{}] with APID {}",
tm.service(),
tm.subservice(),
tm.apid()
);
} }
} }
pub struct TmFunnelStatic { pub struct TmSinkStatic {
common: TmFunnelCommon, common: TmFunnelCommon,
shared_tm_store: SharedPacketPool, shared_tm_store: SharedPacketPool,
tm_funnel_rx: mpsc::Receiver<PacketInPool>, tm_funnel_rx: mpsc::Receiver<PacketInPool>,
tm_server_tx: mpsc::SyncSender<PacketInPool>, tm_server_tx: mpsc::SyncSender<PacketInPool>,
} }
impl TmFunnelStatic { impl TmSinkStatic {
pub fn new( pub fn new(
shared_tm_store: SharedPacketPool, shared_tm_store: SharedPacketPool,
sync_tm_tcp_source: SyncTcpTmSource, sync_tm_tcp_source: SyncTcpTmSource,
@ -121,13 +126,13 @@ impl TmFunnelStatic {
} }
} }
pub struct TmFunnelDynamic { pub struct TmSinkDynamic {
common: TmFunnelCommon, common: TmFunnelCommon,
tm_funnel_rx: mpsc::Receiver<PacketAsVec>, tm_funnel_rx: mpsc::Receiver<PacketAsVec>,
tm_server_tx: mpsc::Sender<PacketAsVec>, tm_server_tx: mpsc::Sender<PacketAsVec>,
} }
impl TmFunnelDynamic { impl TmSinkDynamic {
pub fn new( pub fn new(
sync_tm_tcp_source: SyncTcpTmSource, sync_tm_tcp_source: SyncTcpTmSource,
tm_funnel_rx: mpsc::Receiver<PacketAsVec>, tm_funnel_rx: mpsc::Receiver<PacketAsVec>,

View File

@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.1.4] 2024-04-24
## Added
- `ResultU16::from_be_bytes`
- `From<u16>` impl for `ResultU16`.
- Optional `defmt` support: `defmt::Format` impl on `ResultU16` if the `defmt` feature is
activated.
# [v0.1.3] 2024-04-16 # [v0.1.3] 2024-04-16
Allow `spacepackets` range starting with v0.10 and v0.11. Allow `spacepackets` range starting with v0.10 and v0.11.

View File

@ -1,7 +1,7 @@
[package] [package]
name = "satrs-shared" name = "satrs-shared"
description = "Components shared by multiple sat-rs crates" description = "Components shared by multiple sat-rs crates"
version = "0.1.3" version = "0.1.4"
edition = "2021" edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/" homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
@ -17,12 +17,17 @@ version = "1"
default-features = false default-features = false
optional = true optional = true
[dependencies.defmt]
version = "0.3"
optional = true
[dependencies.spacepackets] [dependencies.spacepackets]
version = ">0.9, <=0.11" version = ">0.9, <=0.11"
default-features = false default-features = false
[features] [features]
serde = ["dep:serde", "spacepackets/serde"] serde = ["dep:serde", "spacepackets/serde"]
spacepackets = ["dep:defmt", "spacepackets/defmt"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"] rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"]

View File

@ -1,3 +1,4 @@
//! This crates contains modules shared among other sat-rs framework crates. //! This crates contains modules shared among other sat-rs framework crates.
#![no_std] #![no_std]
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
pub mod res_code; pub mod res_code;

View File

@ -7,6 +7,7 @@ use spacepackets::ByteConversionError;
/// Simple [u16] based result code type which also allows to group related resultcodes. /// Simple [u16] based result code type which also allows to group related resultcodes.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ResultU16 { pub struct ResultU16 {
group_id: u8, group_id: u8,
unique_id: u8, unique_id: u8,
@ -19,15 +20,28 @@ impl ResultU16 {
unique_id, unique_id,
} }
} }
pub fn raw(&self) -> u16 { pub fn raw(&self) -> u16 {
((self.group_id as u16) << 8) | self.unique_id as u16 ((self.group_id as u16) << 8) | self.unique_id as u16
} }
pub fn group_id(&self) -> u8 { pub fn group_id(&self) -> u8 {
self.group_id self.group_id
} }
pub fn unique_id(&self) -> u8 { pub fn unique_id(&self) -> u8 {
self.unique_id self.unique_id
} }
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
Self::from(u16::from_be_bytes(bytes))
}
}
impl From<u16> for ResultU16 {
fn from(value: u16) -> Self {
Self::new(((value >> 8) & 0xff) as u8, (value & 0xff) as u8)
}
} }
impl From<ResultU16> for EcssEnumU16 { impl From<ResultU16> for EcssEnumU16 {
@ -84,5 +98,14 @@ mod tests {
assert_eq!(written, 2); assert_eq!(written, 2);
assert_eq!(buf[0], 1); assert_eq!(buf[0], 1);
assert_eq!(buf[1], 1); assert_eq!(buf[1], 1);
let read_back = ResultU16::from_be_bytes(buf);
assert_eq!(read_back, result_code);
}
#[test]
fn test_from_u16() {
let result_code = ResultU16::new(1, 1);
let result_code_2 = ResultU16::from(result_code.raw());
assert_eq!(result_code, result_code_2);
} }
} }

View File

@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.2.0] 2024-05-02
## Changed
- Various improvements for the PUS stack components.
## Added
- Added `HandlingStatus` enumeration.
# [v0.2.0-rc.5] 2024-04-24
## Added
- Optional `defmt::Format` support for the event types, if the `defmt` feature is activated.
## Changed
- Removed `MpscEventReceiver`, the `EventReceiveProvider` trait is implemented directly
on `mpsc::Receiver<EventMessage<Event>>`
- Renamed `PusEventDispatcher` to `PusEventTmCreatorWithMap`.
- Renamed `DefaultPusEventU32Dispatcher` to `DefaultPusEventU32EventCreator`.
- Renamed `PusEventMgmtBackendProvider` renamed to `PusEventReportingMap`.
- Reanmed Event `const_new` methods to `new` and the former `new` methods to `new_checked`
# [v0.2.0-rc.4] 2024-04-23 # [v0.2.0-rc.4] 2024-04-23
## Changed ## Changed

View File

@ -1,6 +1,6 @@
[package] [package]
name = "satrs" name = "satrs"
version = "0.2.0-rc.4" version = "0.2.0"
edition = "2021" edition = "2021"
rust-version = "1.71.1" rust-version = "1.71.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
@ -15,6 +15,7 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
[dependencies] [dependencies]
delegate = ">0.7, <=0.10" delegate = ">0.7, <=0.10"
paste = "1" paste = "1"
derive-new = "0.6"
smallvec = "1" smallvec = "1"
crc = "3" crc = "3"
@ -84,10 +85,14 @@ version = "0.8"
features = ["os-poll", "net"] features = ["os-poll", "net"]
optional = true optional = true
[dependencies.defmt]
version = "0.3"
optional = true
[dev-dependencies] [dev-dependencies]
serde = "1" serde = "1"
zerocopy = "0.7" zerocopy = "0.7"
once_cell = "1.13" once_cell = "1"
serde_json = "1" serde_json = "1"
rand = "0.8" rand = "0.8"
tempfile = "3" tempfile = "3"
@ -120,7 +125,7 @@ alloc = [
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"] serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
crossbeam = ["crossbeam-channel"] crossbeam = ["crossbeam-channel"]
heapless = ["dep:heapless"] heapless = ["dep:heapless"]
defmt = ["spacepackets/defmt"] defmt = ["dep:defmt", "spacepackets/defmt"]
test_util = [] test_util = []
doc-images = [] doc-images = []

View File

@ -1,14 +1,12 @@
//! Event management and forwarding //! Event management and forwarding
//! //!
//! 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. One common use case for satellite systems is to offer a light-weight
//! publish-subscribe mechanism and IPC mechanism for software and hardware events which are also
//! packaged as telemetry (TM) or can trigger a system response.
//!
//! It is recommended to read the //! It is recommended to read the
//! [sat-rs book chapter](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/events.html) //! [sat-rs book chapter](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/events.html)
//! about events first: //! 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 //! The event manager has a listener table abstracted by the [ListenerMapProvider], which maps
//! listener groups identified by [ListenerKey]s to a [listener ID][ComponentId]. //! listener groups identified by [ListenerKey]s to a [listener ID][ComponentId].
@ -21,8 +19,8 @@
//! //!
//! 1. Provide a concrete [EventReceiveProvider] implementation. This abstraction allow to use different //! 1. Provide a concrete [EventReceiveProvider] implementation. This abstraction allow to use different
//! message queue backends. A straightforward implementation where dynamic memory allocation is //! message queue backends. A straightforward implementation where dynamic memory allocation is
//! not a big concern could use [std::sync::mpsc::channel] to do this and is provided in //! not a big concern would be to use the [std::sync::mpsc::Receiver] handle. The trait is
//! form of the [MpscEventReceiver]. //! already implemented for this type.
//! 2. To set up event creators, create channel pairs using some message queue implementation. //! 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 //! Each event creator gets a (cloned) sender component which allows it to send events to the
//! manager. //! manager.
@ -44,6 +42,12 @@
//! You can check [integration test](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs/tests/pus_events.rs) //! 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 //! for a concrete example using multi-threading where events are routed to
//! different threads. //! 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::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw}; use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw};
use crate::params::Params; use crate::params::Params;
use crate::queue::GenericSendError; use crate::queue::GenericSendError;
@ -157,9 +161,10 @@ pub trait SenderMapProvider<
/// * `SenderMap`: [SenderMapProvider] which maps channel IDs to send providers. /// * `SenderMap`: [SenderMapProvider] which maps channel IDs to send providers.
/// * `ListenerMap`: [ListenerMapProvider] which maps listener keys to channel IDs. /// * `ListenerMap`: [ListenerMapProvider] which maps listener keys to channel IDs.
/// * `EventSender`: [EventSendProvider] contained within the sender map which sends the events. /// * `EventSender`: [EventSendProvider] contained within the sender map which sends the events.
/// * `Ev`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32] /// * `Event`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32]
/// and [EventU16] are supported. /// and [EventU16] are supported.
/// * `Data`: Auxiliary data which is sent with the event to provide optional context information /// * `ParamProvider`: Auxiliary data which is sent with the event to provide optional context
/// information
pub struct EventManager< pub struct EventManager<
EventReceiver: EventReceiveProvider<Event, ParamProvider>, EventReceiver: EventReceiveProvider<Event, ParamProvider>,
SenderMap: SenderMapProvider<EventSender, Event, ParamProvider>, SenderMap: SenderMapProvider<EventSender, Event, ParamProvider>,
@ -290,7 +295,7 @@ impl<
for id in ids { for id in ids {
if let Some(sender) = self.sender_map.get_send_event_provider(id) { if let Some(sender) = self.sender_map.get_send_event_provider(id) {
if let Err(e) = sender.send(EventMessage::new_generic( if let Err(e) = sender.send(EventMessage::new_generic(
*id, event_msg.sender_id,
event_msg.event, event_msg.event,
event_msg.params.as_ref(), event_msg.params.as_ref(),
)) { )) {
@ -331,11 +336,11 @@ pub mod alloc_mod {
/// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap] /// 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. /// and the [DefaultListenerMap]. It uses regular mpsc channels as the message queue backend.
pub type EventManagerWithMpsc<EV = EventU32, AUX = Params> = EventManager< pub type EventManagerWithMpsc<Event = EventU32, ParamProvider = Params> = EventManager<
MpscEventReceiver, EventU32ReceiverMpsc<ParamProvider>,
DefaultSenderMap<EventSenderMpsc<EV>, EV, AUX>, DefaultSenderMap<EventSenderMpsc<Event>, Event, ParamProvider>,
DefaultListenerMap, DefaultListenerMap,
EventSenderMpsc<EV>, EventSenderMpsc<Event>,
>; >;
/// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap] /// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap]
@ -343,7 +348,7 @@ pub mod alloc_mod {
/// [bounded mpsc senders](https://doc.rust-lang.org/std/sync/mpsc/struct.SyncSender.html) as the /// [bounded mpsc senders](https://doc.rust-lang.org/std/sync/mpsc/struct.SyncSender.html) as the
/// message queue backend. /// message queue backend.
pub type EventManagerWithBoundedMpsc<Event = EventU32, ParamProvider = Params> = EventManager< pub type EventManagerWithBoundedMpsc<Event = EventU32, ParamProvider = Params> = EventManager<
MpscEventReceiver, EventU32ReceiverMpsc<ParamProvider>,
DefaultSenderMap<EventSenderMpscBounded<Event>, Event, ParamProvider>, DefaultSenderMap<EventSenderMpscBounded<Event>, Event, ParamProvider>,
DefaultListenerMap, DefaultListenerMap,
EventSenderMpscBounded<Event>, EventSenderMpscBounded<Event>,
@ -479,20 +484,16 @@ pub mod std_mod {
use super::*; use super::*;
use std::sync::mpsc; use std::sync::mpsc;
pub struct MpscEventReceiver<Event: GenericEvent + Send = EventU32> { impl<Event: GenericEvent + Send, ParamProvider: Debug>
receiver: mpsc::Receiver<EventMessage<Event>>, EventReceiveProvider<Event, ParamProvider>
} for mpsc::Receiver<EventMessage<Event, ParamProvider>>
{
impl<Event: GenericEvent + Send> MpscEventReceiver<Event> {
pub fn new(receiver: mpsc::Receiver<EventMessage<Event>>) -> Self {
Self { receiver }
}
}
impl<Event: GenericEvent + Send> EventReceiveProvider<Event> for MpscEventReceiver<Event> {
type Error = GenericReceiveError; type Error = GenericReceiveError;
fn try_recv_event(&self) -> Result<Option<EventMessage<Event>>, Self::Error> { fn try_recv_event(
match self.receiver.try_recv() { &self,
) -> Result<Option<EventMessage<Event, ParamProvider>>, Self::Error> {
match self.try_recv() {
Ok(msg) => Ok(Some(msg)), Ok(msg) => Ok(Some(msg)),
Err(e) => match e { Err(e) => match e {
mpsc::TryRecvError::Empty => Ok(None), mpsc::TryRecvError::Empty => Ok(None),
@ -504,8 +505,10 @@ pub mod std_mod {
} }
} }
pub type MpscEventU32Receiver = MpscEventReceiver<EventU32>; pub type EventU32ReceiverMpsc<ParamProvider = Params> =
pub type MpscEventU16Receiver = MpscEventReceiver<EventU16>; 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 /// Generic event sender which uses a regular [mpsc::Sender] as the messaging backend to
/// send events. /// send events.
@ -594,7 +597,7 @@ mod tests {
use std::format; use std::format;
use std::sync::mpsc::{self}; use std::sync::mpsc::{self};
const TEST_EVENT: EventU32 = EventU32::const_new(Severity::INFO, 0, 5); const TEST_EVENT: EventU32 = EventU32::new(Severity::Info, 0, 5);
fn check_next_event( fn check_next_event(
expected: EventU32, expected: EventU32,
@ -611,6 +614,7 @@ mod tests {
res: EventRoutingResult<EventU32, Params>, res: EventRoutingResult<EventU32, Params>,
expected: EventU32, expected: EventU32,
expected_num_sent: u32, expected_num_sent: u32,
expected_sender_id: ComponentId,
) { ) {
assert!(matches!(res, EventRoutingResult::Handled { .. })); assert!(matches!(res, EventRoutingResult::Handled { .. }));
if let EventRoutingResult::Handled { if let EventRoutingResult::Handled {
@ -619,21 +623,21 @@ mod tests {
} = res } = res
{ {
assert_eq!(event_msg.event, expected); assert_eq!(event_msg.event, expected);
assert_eq!(event_msg.sender_id, expected_sender_id);
assert_eq!(num_recipients, expected_num_sent); assert_eq!(num_recipients, expected_num_sent);
} }
} }
fn generic_event_man() -> (mpsc::Sender<EventMessageU32>, EventManagerWithMpsc) { fn generic_event_man() -> (mpsc::Sender<EventMessageU32>, EventManagerWithMpsc) {
let (event_sender, manager_queue) = mpsc::channel(); let (event_sender, event_receiver) = mpsc::channel();
let event_man_receiver = MpscEventReceiver::new(manager_queue); (event_sender, EventManager::new(event_receiver))
(event_sender, EventManager::new(event_man_receiver))
} }
#[test] #[test]
fn test_basic() { fn test_basic() {
let (event_sender, mut event_man) = generic_event_man(); let (event_sender, mut event_man) = generic_event_man();
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); let event_grp_1_0 = EventU32::new(Severity::High, 1, 0);
let (single_event_sender, single_event_receiver) = mpsc::channel(); let (single_event_sender, single_event_receiver) = mpsc::channel();
let single_event_listener = EventSenderMpsc::new(0, single_event_sender); let single_event_listener = EventSenderMpsc::new(0, single_event_sender);
event_man.subscribe_single(&event_grp_0, single_event_listener.target_id()); event_man.subscribe_single(&event_grp_0, single_event_listener.target_id());
@ -651,8 +655,7 @@ mod tests {
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0)) .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0))
.expect("Sending single error failed"); .expect("Sending single error failed");
let res = event_man.try_event_handling(&error_handler); let res = event_man.try_event_handling(&error_handler);
// assert!(res.is_ok()); check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
check_handled_event(res, event_grp_0, 1);
check_next_event(event_grp_0, &single_event_receiver); check_next_event(event_grp_0, &single_event_receiver);
// Test event which is sent to all group listeners // Test event which is sent to all group listeners
@ -660,7 +663,7 @@ mod tests {
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0)) .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0))
.expect("Sending group error failed"); .expect("Sending group error failed");
let res = event_man.try_event_handling(&error_handler); let res = event_man.try_event_handling(&error_handler);
check_handled_event(res, event_grp_1_0, 1); 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); check_next_event(event_grp_1_0, &group_event_receiver_0);
} }
@ -670,7 +673,7 @@ mod tests {
panic!("routing error occurred for event {:?}: {:?}", event_msg, e); panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
}; };
let (event_sender, mut event_man) = generic_event_man(); let (event_sender, mut event_man) = generic_event_man();
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
let (single_event_sender, single_event_receiver) = mpsc::channel(); let (single_event_sender, single_event_receiver) = mpsc::channel();
let single_event_listener = EventSenderMpsc::new(0, single_event_sender); let single_event_listener = EventSenderMpsc::new(0, single_event_sender);
event_man.subscribe_single(&event_grp_0, single_event_listener.target_id()); event_man.subscribe_single(&event_grp_0, single_event_listener.target_id());
@ -683,7 +686,7 @@ mod tests {
)) ))
.expect("Sending group error failed"); .expect("Sending group error failed");
let res = event_man.try_event_handling(&error_handler); let res = event_man.try_event_handling(&error_handler);
check_handled_event(res, event_grp_0, 1); check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
let aux = check_next_event(event_grp_0, &single_event_receiver); let aux = check_next_event(event_grp_0, &single_event_receiver);
assert!(aux.is_some()); assert!(aux.is_some());
let aux = aux.unwrap(); let aux = aux.unwrap();
@ -705,8 +708,8 @@ mod tests {
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
assert!(matches!(res, EventRoutingResult::Empty)); assert!(matches!(res, EventRoutingResult::Empty));
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); let event_grp_0 = EventU32::new(Severity::Info, 0, 0);
let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); 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_sender, event_grp_0_receiver) = mpsc::channel();
let event_grp_0_and_1_listener = EventU32SenderMpsc::new(0, event_grp_0_sender); let event_grp_0_and_1_listener = EventU32SenderMpsc::new(0, event_grp_0_sender);
event_man.subscribe_group( event_man.subscribe_group(
@ -726,9 +729,9 @@ mod tests {
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0)) .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0))
.expect("Sendign Event Group 1 failed"); .expect("Sendign Event Group 1 failed");
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_grp_0, 1); check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id());
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_grp_1_0, 1); 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_0, &event_grp_0_receiver);
check_next_event(event_grp_1_0, &event_grp_0_receiver); check_next_event(event_grp_1_0, &event_grp_0_receiver);
@ -742,8 +745,8 @@ mod tests {
panic!("routing error occurred for event {:?}: {:?}", event_msg, e); panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
}; };
let (event_sender, mut event_man) = generic_event_man(); let (event_sender, mut event_man) = generic_event_man();
let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); let event_0 = EventU32::new(Severity::Info, 0, 5);
let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); let event_1 = EventU32::new(Severity::High, 1, 0);
let (event_0_tx_0, event_0_rx_0) = mpsc::channel(); let (event_0_tx_0, event_0_rx_0) = mpsc::channel();
let (event_0_tx_1, event_0_rx_1) = 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_0 = EventU32SenderMpsc::new(0, event_0_tx_0);
@ -758,7 +761,7 @@ mod tests {
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0)) .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
.expect("Triggering Event 0 failed"); .expect("Triggering Event 0 failed");
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_0, 2); 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_0);
check_next_event(event_0, &event_0_rx_1); check_next_event(event_0, &event_0_rx_1);
event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id); event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
@ -771,9 +774,9 @@ mod tests {
// 3 Events messages will be sent now // 3 Events messages will be sent now
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_0, 2); check_handled_event(res, event_0, 2, TEST_COMPONENT_ID_0.id());
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_1, 1); check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_1.id());
// Both the single event and the group event should arrive now // Both the single event and the group event should arrive now
check_next_event(event_0, &event_0_rx_0); check_next_event(event_0, &event_0_rx_0);
check_next_event(event_1, &event_0_rx_0); check_next_event(event_1, &event_0_rx_0);
@ -785,7 +788,7 @@ mod tests {
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_1)) .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_1))
.expect("Triggering Event 1 failed"); .expect("Triggering Event 1 failed");
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_1, 1); check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_0.id());
} }
#[test] #[test]
@ -793,11 +796,10 @@ mod tests {
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| { let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
panic!("routing error occurred for event {:?}: {:?}", event_msg, e); panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
}; };
let (event_sender, manager_queue) = mpsc::channel(); let (event_sender, event_receiver) = mpsc::channel();
let event_man_receiver = MpscEventReceiver::new(manager_queue); let mut event_man = EventManagerWithMpsc::new(event_receiver);
let mut event_man = EventManagerWithMpsc::new(event_man_receiver); let event_0 = EventU32::new(Severity::Info, 0, 5);
let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); let event_1 = EventU32::new(Severity::High, 1, 0);
let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
let (event_0_tx_0, all_events_rx) = mpsc::channel(); let (event_0_tx_0, all_events_rx) = mpsc::channel();
let all_events_listener = EventU32SenderMpsc::new(0, event_0_tx_0); let all_events_listener = EventU32SenderMpsc::new(0, event_0_tx_0);
event_man.subscribe_all(all_events_listener.target_id()); event_man.subscribe_all(all_events_listener.target_id());
@ -809,9 +811,9 @@ mod tests {
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1)) .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1))
.expect("Triggering event 1 failed"); .expect("Triggering event 1 failed");
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_0, 1); check_handled_event(res, event_0, 1, TEST_COMPONENT_ID_0.id());
let res = event_man.try_event_handling(error_handler); let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_1, 1); check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_1.id());
check_next_event(event_0, &all_events_rx); check_next_event(event_0, &all_events_rx);
check_next_event(event_1, &all_events_rx); check_next_event(event_1, &all_events_rx);
} }

View File

@ -20,12 +20,12 @@
//! ``` //! ```
//! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo}; //! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo};
//! //!
//! const MSG_RECVD: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::const_new(1, 0); //! const MSG_RECVD: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(1, 0);
//! const MSG_FAILED: EventU32 = EventU32::const_new(Severity::LOW, 1, 1); //! const MSG_FAILED: EventU32 = EventU32::new(Severity::Low, 1, 1);
//! //!
//! const TEMPERATURE_HIGH: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::const_new(2, 0); //! const TEMPERATURE_HIGH: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::new(2, 0);
//! //!
//! let small_event = EventU16::new(Severity::INFO, 3, 0); //! let small_event = EventU16::new(Severity::Info, 3, 0);
//! ``` //! ```
use core::fmt::Debug; use core::fmt::Debug;
use core::hash::Hash; use core::hash::Hash;
@ -40,12 +40,17 @@ pub type LargestEventRaw = u32;
/// Using a type definition allows to change this to u32 in the future more easily /// Using a type definition allows to change this to u32 in the future more easily
pub type LargestGroupIdRaw = u16; 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)] #[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 { pub enum Severity {
INFO = 0, Info = 0,
LOW = 1, Low = 1,
MEDIUM = 2, Medium = 2,
HIGH = 3, High = 3,
} }
pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone { pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone {
@ -56,28 +61,28 @@ pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone {
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct SeverityInfo {} pub struct SeverityInfo {}
impl HasSeverity for SeverityInfo { impl HasSeverity for SeverityInfo {
const SEVERITY: Severity = Severity::INFO; const SEVERITY: Severity = Severity::Info;
} }
/// Type level support struct /// Type level support struct
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct SeverityLow {} pub struct SeverityLow {}
impl HasSeverity for SeverityLow { impl HasSeverity for SeverityLow {
const SEVERITY: Severity = Severity::LOW; const SEVERITY: Severity = Severity::Low;
} }
/// Type level support struct /// Type level support struct
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct SeverityMedium {} pub struct SeverityMedium {}
impl HasSeverity for SeverityMedium { impl HasSeverity for SeverityMedium {
const SEVERITY: Severity = Severity::MEDIUM; const SEVERITY: Severity = Severity::Medium;
} }
/// Type level support struct /// Type level support struct
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct SeverityHigh {} pub struct SeverityHigh {}
impl HasSeverity for SeverityHigh { impl HasSeverity for SeverityHigh {
const SEVERITY: Severity = Severity::HIGH; const SEVERITY: Severity = Severity::High;
} }
pub trait GenericEvent: EcssEnumeration + Copy + Clone { pub trait GenericEvent: EcssEnumeration + Copy + Clone {
@ -99,27 +104,29 @@ impl TryFrom<u8> for Severity {
fn try_from(value: u8) -> Result<Self, Self::Error> { fn try_from(value: u8) -> Result<Self, Self::Error> {
match value { match value {
x if x == Severity::INFO as u8 => Ok(Severity::INFO), x if x == Severity::Info as u8 => Ok(Severity::Info),
x if x == Severity::LOW as u8 => Ok(Severity::LOW), x if x == Severity::Low as u8 => Ok(Severity::Low),
x if x == Severity::MEDIUM as u8 => Ok(Severity::MEDIUM), x if x == Severity::Medium as u8 => Ok(Severity::Medium),
x if x == Severity::HIGH as u8 => Ok(Severity::HIGH), x if x == Severity::High as u8 => Ok(Severity::High),
_ => Err(()), _ => Err(()),
} }
} }
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
struct EventBase<RAW, GID, UID> { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct EventBase<Raw, GroupId, UniqueId> {
severity: Severity, severity: Severity,
group_id: GID, group_id: GroupId,
unique_id: UID, unique_id: UniqueId,
phantom: PhantomData<RAW>, phantom: PhantomData<Raw>,
} }
impl<RAW: ToBeBytes, GID, UID> EventBase<RAW, GID, UID> { impl<Raw: ToBeBytes, GroupId, UniqueId> EventBase<Raw, GroupId, UniqueId> {
fn write_to_bytes( fn write_to_bytes(
&self, &self,
raw: RAW, raw: Raw,
buf: &mut [u8], buf: &mut [u8],
width: usize, width: usize,
) -> Result<usize, ByteConversionError> { ) -> Result<usize, ByteConversionError> {
@ -267,6 +274,7 @@ macro_rules! const_from_fn {
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EventU32 { pub struct EventU32 {
base: EventBase<u32, u16, u16>, base: EventBase<u32, u16, u16>,
} }
@ -309,12 +317,12 @@ impl EventU32 {
/// next 14 bits after the severity. Therefore, the size is limited by dec 16383 hex 0x3FFF. /// 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 /// * `unique_id`: Each event has a unique 16 bit ID occupying the last 16 bits of the
/// raw event ID /// raw event ID
pub fn new( pub fn new_checked(
severity: Severity, severity: Severity,
group_id: <Self as GenericEvent>::GroupId, group_id: <Self as GenericEvent>::GroupId,
unique_id: <Self as GenericEvent>::UniqueId, unique_id: <Self as GenericEvent>::UniqueId,
) -> Option<Self> { ) -> Option<Self> {
if group_id > (2u16.pow(14) - 1) { if group_id > MAX_GROUP_ID_U32_EVENT {
return None; return None;
} }
Some(Self { Some(Self {
@ -326,12 +334,14 @@ impl EventU32 {
}, },
}) })
} }
pub const fn const_new(
/// This constructor will panic if the passed group is is larger than [MAX_GROUP_ID_U32_EVENT].
pub const fn new(
severity: Severity, severity: Severity,
group_id: <Self as GenericEvent>::GroupId, group_id: <Self as GenericEvent>::GroupId,
unique_id: <Self as GenericEvent>::UniqueId, unique_id: <Self as GenericEvent>::UniqueId,
) -> Self { ) -> Self {
if group_id > (2u16.pow(14) - 1) { if group_id > MAX_GROUP_ID_U32_EVENT {
panic!("Group ID too large"); panic!("Group ID too large");
} }
Self { Self {
@ -344,50 +354,16 @@ impl EventU32 {
} }
} }
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_info, EventU32TypedSev, SeverityInfo);
const_from_fn!(const_from_low, EventU32TypedSev, SeverityLow); const_from_fn!(const_from_low, EventU32TypedSev, SeverityLow);
const_from_fn!(const_from_medium, EventU32TypedSev, SeverityMedium); const_from_fn!(const_from_medium, EventU32TypedSev, SeverityMedium);
const_from_fn!(const_from_high, EventU32TypedSev, SeverityHigh); const_from_fn!(const_from_high, EventU32TypedSev, SeverityHigh);
} }
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(
group_id: <Self as GenericEvent>::GroupId,
unique_id: <Self as GenericEvent>::UniqueId,
) -> Option<Self> {
let event = EventU32::new(SEVERITY::SEVERITY, group_id, unique_id)?;
Some(Self {
event,
phantom: PhantomData,
})
}
/// Const version of [Self::new], but panics on invalid group ID input values.
pub const fn const_new(
group_id: <Self as GenericEvent>::GroupId,
unique_id: <Self as GenericEvent>::UniqueId,
) -> Self {
let event = EventU32::const_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::const_new(
((raw >> 16) & 0x3FFF) as u16,
(raw & 0xFFFF) as u16,
))
}
}
impl From<u32> for EventU32 { impl From<u32> for EventU32 {
fn from(raw: u32) -> Self { fn from(raw: u32) -> Self {
// Severity conversion from u8 should never fail // Severity conversion from u8 should never fail
@ -395,15 +371,10 @@ impl From<u32> for EventU32 {
let group_id = ((raw >> 16) & 0x3FFF) as u16; let group_id = ((raw >> 16) & 0x3FFF) as u16;
let unique_id = (raw & 0xFFFF) as u16; let unique_id = (raw & 0xFFFF) as u16;
// Sanitized input, should never fail // Sanitized input, should never fail
Self::const_new(severity, group_id, unique_id) Self::new(severity, group_id, unique_id)
} }
} }
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);
impl UnsignedEnum for EventU32 { impl UnsignedEnum for EventU32 {
fn size(&self) -> usize { fn size(&self) -> usize {
core::mem::size_of::<u32>() core::mem::size_of::<u32>()
@ -424,6 +395,49 @@ impl EcssEnumeration for EventU32 {
} }
} }
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 //noinspection RsTraitImplementation
impl<SEVERITY: HasSeverity> UnsignedEnum for EventU32TypedSev<SEVERITY> { impl<SEVERITY: HasSeverity> UnsignedEnum for EventU32TypedSev<SEVERITY> {
delegate!(to self.event { delegate!(to self.event {
@ -441,6 +455,8 @@ impl<SEVERITY: HasSeverity> EcssEnumeration for EventU32TypedSev<SEVERITY> {
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[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 { pub struct EventU16 {
base: EventBase<u16, u8, u8>, base: EventBase<u16, u8, u8>,
} }
@ -475,7 +491,7 @@ impl EventU16 {
/// next 6 bits after the severity. Therefore, the size is limited by dec 63 hex 0x3F. /// 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 /// * `unique_id`: Each event has a unique 8 bit ID occupying the last 8 bits of the
/// raw event ID /// raw event ID
pub fn new( pub fn new_checked(
severity: Severity, severity: Severity,
group_id: <Self as GenericEvent>::GroupId, group_id: <Self as GenericEvent>::GroupId,
unique_id: <Self as GenericEvent>::UniqueId, unique_id: <Self as GenericEvent>::UniqueId,
@ -493,8 +509,8 @@ impl EventU16 {
}) })
} }
/// Const version of [Self::new], but panics on invalid group ID input values. /// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT].
pub const fn const_new( pub const fn new(
severity: Severity, severity: Severity,
group_id: <Self as GenericEvent>::GroupId, group_id: <Self as GenericEvent>::GroupId,
unique_id: <Self as GenericEvent>::UniqueId, unique_id: <Self as GenericEvent>::UniqueId,
@ -511,52 +527,26 @@ impl EventU16 {
}, },
} }
} }
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_info, EventU16TypedSev, SeverityInfo);
const_from_fn!(const_from_low, EventU16TypedSev, SeverityLow); const_from_fn!(const_from_low, EventU16TypedSev, SeverityLow);
const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium); const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium);
const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh); const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh);
} }
impl<SEVERITY: HasSeverity> EventU16TypedSev<SEVERITY> { impl From<u16> for EventU16 {
/// This is similar to [EventU16::new] but the severity is a type generic, which allows to fn from(raw: <Self as GenericEvent>::Raw) -> Self {
/// have distinct types for events with different severities
pub fn new(
group_id: <Self as GenericEvent>::GroupId,
unique_id: <Self as GenericEvent>::UniqueId,
) -> Option<Self> {
let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id)?;
Some(Self {
event,
phantom: PhantomData,
})
}
/// Const version of [Self::new], but panics on invalid group ID input values.
pub const fn const_new(
group_id: <Self as GenericEvent>::GroupId,
unique_id: <Self as GenericEvent>::UniqueId,
) -> Self {
let event = EventU16::const_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(); let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
if severity != expected { let group_id = ((raw >> 8) & 0x3F) as u8;
return Err(severity); let unique_id = (raw & 0xFF) as u8;
} // Sanitized input, new call should never fail
Ok(Self::const_new( Self::new(severity, group_id, unique_id)
((raw >> 8) & 0x3F) as u8,
(raw & 0xFF) as u8,
))
} }
} }
impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8);
impl UnsignedEnum for EventU16 { impl UnsignedEnum for EventU16 {
fn size(&self) -> usize { fn size(&self) -> usize {
core::mem::size_of::<u16>() core::mem::size_of::<u16>()
@ -577,6 +567,43 @@ impl EcssEnumeration for EventU16 {
} }
} }
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 //noinspection RsTraitImplementation
impl<SEVERITY: HasSeverity> UnsignedEnum for EventU16TypedSev<SEVERITY> { impl<SEVERITY: HasSeverity> UnsignedEnum for EventU16TypedSev<SEVERITY> {
delegate!(to self.event { delegate!(to self.event {
@ -593,20 +620,10 @@ impl<SEVERITY: HasSeverity> EcssEnumeration for EventU16TypedSev<SEVERITY> {
}); });
} }
impl From<u16> for EventU16 { try_from_impls!(SeverityInfo, Severity::Info, u16, EventU16TypedSev);
fn from(raw: <Self as GenericEvent>::Raw) -> Self { try_from_impls!(SeverityLow, Severity::Low, u16, EventU16TypedSev);
let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap(); try_from_impls!(SeverityMedium, Severity::Medium, u16, EventU16TypedSev);
let group_id = ((raw >> 8) & 0x3F) as u8; try_from_impls!(SeverityHigh, Severity::High, u16, EventU16TypedSev);
let unique_id = (raw & 0xFF) as u8;
// Sanitized input, new call should never fail
Self::const_new(severity, group_id, unique_id)
}
}
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> { impl<Severity: HasSeverity> PartialEq<EventU32> for EventU32TypedSev<Severity> {
#[inline] #[inline]
@ -647,12 +664,10 @@ mod tests {
assert_eq!(size_of::<T>(), val); assert_eq!(size_of::<T>(), val);
} }
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::const_new(0, 0); const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(0, 0);
const INFO_EVENT_SMALL: EventU16TypedSev<SeverityInfo> = EventU16TypedSev::const_new(0, 0); const INFO_EVENT_SMALL: EventU16TypedSev<SeverityInfo> = EventU16TypedSev::new(0, 0);
const HIGH_SEV_EVENT: EventU32TypedSev<SeverityHigh> = const HIGH_SEV_EVENT: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::new(0x3FFF, 0xFFFF);
EventU32TypedSev::const_new(0x3FFF, 0xFFFF); const HIGH_SEV_EVENT_SMALL: EventU16TypedSev<SeverityHigh> = EventU16TypedSev::new(0x3F, 0xff);
const HIGH_SEV_EVENT_SMALL: EventU16TypedSev<SeverityHigh> =
EventU16TypedSev::const_new(0x3F, 0xff);
/// This working is a test in itself. /// This working is a test in itself.
const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT); const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT);
@ -683,7 +698,7 @@ mod tests {
#[test] #[test]
fn test_normal_event_getters() { fn test_normal_event_getters() {
assert_eq!(INFO_EVENT.severity(), Severity::INFO); assert_eq!(INFO_EVENT.severity(), Severity::Info);
assert_eq!(INFO_EVENT.unique_id(), 0); assert_eq!(INFO_EVENT.unique_id(), 0);
assert_eq!(INFO_EVENT.group_id(), 0); assert_eq!(INFO_EVENT.group_id(), 0);
let raw_event = INFO_EVENT.raw(); let raw_event = INFO_EVENT.raw();
@ -692,7 +707,7 @@ mod tests {
#[test] #[test]
fn test_small_event_getters() { fn test_small_event_getters() {
assert_eq!(INFO_EVENT_SMALL.severity(), Severity::INFO); assert_eq!(INFO_EVENT_SMALL.severity(), Severity::Info);
assert_eq!(INFO_EVENT_SMALL.unique_id(), 0); assert_eq!(INFO_EVENT_SMALL.unique_id(), 0);
assert_eq!(INFO_EVENT_SMALL.group_id(), 0); assert_eq!(INFO_EVENT_SMALL.group_id(), 0);
let raw_event = INFO_EVENT_SMALL.raw(); let raw_event = INFO_EVENT_SMALL.raw();
@ -701,7 +716,7 @@ mod tests {
#[test] #[test]
fn all_ones_event_regular() { fn all_ones_event_regular() {
assert_eq!(HIGH_SEV_EVENT.severity(), Severity::HIGH); assert_eq!(HIGH_SEV_EVENT.severity(), Severity::High);
assert_eq!(HIGH_SEV_EVENT.group_id(), 0x3FFF); assert_eq!(HIGH_SEV_EVENT.group_id(), 0x3FFF);
assert_eq!(HIGH_SEV_EVENT.unique_id(), 0xFFFF); assert_eq!(HIGH_SEV_EVENT.unique_id(), 0xFFFF);
let raw_event = HIGH_SEV_EVENT.raw(); let raw_event = HIGH_SEV_EVENT.raw();
@ -710,7 +725,7 @@ mod tests {
#[test] #[test]
fn all_ones_event_small() { fn all_ones_event_small() {
assert_eq!(HIGH_SEV_EVENT_SMALL.severity(), Severity::HIGH); assert_eq!(HIGH_SEV_EVENT_SMALL.severity(), Severity::High);
assert_eq!(HIGH_SEV_EVENT_SMALL.group_id(), 0x3F); assert_eq!(HIGH_SEV_EVENT_SMALL.group_id(), 0x3F);
assert_eq!(HIGH_SEV_EVENT_SMALL.unique_id(), 0xFF); assert_eq!(HIGH_SEV_EVENT_SMALL.unique_id(), 0xFF);
let raw_event = HIGH_SEV_EVENT_SMALL.raw(); let raw_event = HIGH_SEV_EVENT_SMALL.raw();
@ -719,18 +734,19 @@ mod tests {
#[test] #[test]
fn invalid_group_id_normal() { fn invalid_group_id_normal() {
assert!(EventU32TypedSev::<SeverityMedium>::new(2_u16.pow(14), 0).is_none()); assert!(EventU32TypedSev::<SeverityMedium>::new_checked(2_u16.pow(14), 0).is_none());
} }
#[test] #[test]
fn invalid_group_id_small() { fn invalid_group_id_small() {
assert!(EventU16TypedSev::<SeverityMedium>::new(2_u8.pow(6), 0).is_none()); assert!(EventU16TypedSev::<SeverityMedium>::new_checked(2_u8.pow(6), 0).is_none());
} }
#[test] #[test]
fn regular_new() { fn regular_new() {
assert_eq!( assert_eq!(
EventU32TypedSev::<SeverityInfo>::new(0, 0).expect("Creating regular event failed"), EventU32TypedSev::<SeverityInfo>::new_checked(0, 0)
.expect("Creating regular event failed"),
INFO_EVENT INFO_EVENT
); );
} }
@ -738,7 +754,8 @@ mod tests {
#[test] #[test]
fn small_new() { fn small_new() {
assert_eq!( assert_eq!(
EventU16TypedSev::<SeverityInfo>::new(0, 0).expect("Creating regular event failed"), EventU16TypedSev::<SeverityInfo>::new_checked(0, 0)
.expect("Creating regular event failed"),
INFO_EVENT_SMALL INFO_EVENT_SMALL
); );
} }
@ -777,6 +794,8 @@ mod tests {
assert!(HIGH_SEV_EVENT.write_to_be_bytes(&mut buf).is_ok()); assert!(HIGH_SEV_EVENT.write_to_be_bytes(&mut buf).is_ok());
let val_from_raw = u32::from_be_bytes(buf); let val_from_raw = u32::from_be_bytes(buf);
assert_eq!(val_from_raw, 0xFFFFFFFF); assert_eq!(val_from_raw, 0xFFFFFFFF);
let event_read_back = EventU32::from_be_bytes(buf);
assert_eq!(event_read_back, HIGH_SEV_EVENT);
} }
#[test] #[test]
@ -785,6 +804,8 @@ mod tests {
assert!(HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf).is_ok()); assert!(HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf).is_ok());
let val_from_raw = u16::from_be_bytes(buf); let val_from_raw = u16::from_be_bytes(buf);
assert_eq!(val_from_raw, 0xFFFF); 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] #[test]
@ -815,13 +836,13 @@ mod tests {
fn severity_from_invalid_raw_val() { fn severity_from_invalid_raw_val() {
let invalid = 0xFF; let invalid = 0xFF;
assert!(Severity::try_from(invalid).is_err()); assert!(Severity::try_from(invalid).is_err());
let invalid = Severity::HIGH as u8 + 1; let invalid = Severity::High as u8 + 1;
assert!(Severity::try_from(invalid).is_err()); assert!(Severity::try_from(invalid).is_err());
} }
#[test] #[test]
fn reduction() { fn reduction() {
let event = EventU32TypedSev::<SeverityInfo>::const_new(1, 1); let event = EventU32TypedSev::<SeverityInfo>::new(1, 1);
let raw = event.raw(); let raw = event.raw();
let reduced: EventU32 = event.into(); let reduced: EventU32 = event.into();
assert_eq!(reduced.group_id(), 1); assert_eq!(reduced.group_id(), 1);

View File

@ -643,52 +643,18 @@ impl From<&str> for Params {
} }
} }
/// Please note while [WritableToBeBytes] is implemented for [Params], the default implementation impl WritableToBeBytes for ParamsHeapless {
/// will not be able to process the [Params::Store] parameter variant.
impl WritableToBeBytes for Params {
fn written_len(&self) -> usize { fn written_len(&self) -> usize {
match self { match self {
Params::Heapless(p) => match p { ParamsHeapless::Raw(raw) => raw.written_len(),
ParamsHeapless::Raw(raw) => raw.written_len(), ParamsHeapless::EcssEnum(ecss_enum) => ecss_enum.written_len(),
ParamsHeapless::EcssEnum(enumeration) => enumeration.written_len(),
},
Params::Store(_) => 0,
#[cfg(feature = "alloc")]
Params::Vec(vec) => vec.len(),
#[cfg(feature = "alloc")]
Params::String(string) => string.len(),
} }
} }
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
match self { match self {
Params::Heapless(p) => match p { ParamsHeapless::Raw(raw) => raw.write_to_be_bytes(buf),
ParamsHeapless::Raw(raw) => raw.write_to_be_bytes(buf), ParamsHeapless::EcssEnum(ecss_enum) => ecss_enum.write_to_be_bytes(buf),
ParamsHeapless::EcssEnum(enumeration) => enumeration.write_to_be_bytes(buf),
},
Params::Store(_) => Ok(0),
#[cfg(feature = "alloc")]
Params::Vec(vec) => {
if buf.len() < vec.len() {
return Err(ByteConversionError::ToSliceTooSmall {
found: buf.len(),
expected: vec.len(),
});
}
buf[0..vec.len()].copy_from_slice(vec);
Ok(vec.len())
}
#[cfg(feature = "alloc")]
Params::String(string) => {
if buf.len() < string.len() {
return Err(ByteConversionError::ToSliceTooSmall {
found: buf.len(),
expected: string.len(),
});
}
buf[0..string.len()].copy_from_slice(string.as_bytes());
Ok(string.len())
}
} }
} }
} }
@ -837,10 +803,9 @@ mod tests {
#[test] #[test]
fn test_params_written_len_raw() { fn test_params_written_len_raw() {
let param_raw = ParamsRaw::from((500_u32, 1000_u32)); let param_raw = ParamsRaw::from((500_u32, 1000_u32));
let param: Params = Params::Heapless(param_raw.into()); assert_eq!(param_raw.written_len(), 8);
assert_eq!(param.written_len(), 8);
let mut buf: [u8; 8] = [0; 8]; let mut buf: [u8; 8] = [0; 8];
param param_raw
.write_to_be_bytes(&mut buf) .write_to_be_bytes(&mut buf)
.expect("writing to buffer failed"); .expect("writing to buffer failed");
assert_eq!(u32::from_be_bytes(buf[0..4].try_into().unwrap()), 500); assert_eq!(u32::from_be_bytes(buf[0..4].try_into().unwrap()), 500);
@ -848,21 +813,28 @@ mod tests {
} }
#[test] #[test]
fn test_params_written_string() { fn test_heapless_param_writable_trait_raw() {
let string = "Test String".to_string(); let param_heapless = ParamsHeapless::Raw(ParamsRaw::from((500_u32, 1000_u32)));
let param = Params::String(string.clone()); assert_eq!(param_heapless.written_len(), 8);
assert_eq!(param.written_len(), string.len()); let mut buf: [u8; 8] = [0; 8];
let vec = param.to_vec().unwrap(); let size = param_heapless
let string_conv_back = String::from_utf8(vec).expect("conversion to string failed"); .write_to_be_bytes(&mut buf)
assert_eq!(string_conv_back, string); .expect("writing failed");
assert_eq!(size, 8);
assert_eq!(u32::from_be_bytes(buf[0..4].try_into().unwrap()), 500);
assert_eq!(u32::from_be_bytes(buf[4..8].try_into().unwrap()), 1000);
} }
#[test] #[test]
fn test_params_written_vec() { fn test_heapless_param_writable_trait_ecss_enum() {
let vec: Vec<u8> = alloc::vec![1, 2, 3, 4, 5]; let param_heapless = ParamsHeapless::EcssEnum(ParamsEcssEnum::U16(5.into()));
let param = Params::Vec(vec.clone()); assert_eq!(param_heapless.written_len(), 2);
assert_eq!(param.written_len(), vec.len()); let mut buf: [u8; 2] = [0; 2];
assert_eq!(param.to_vec().expect("writing vec params failed"), vec); let size = param_heapless
.write_to_be_bytes(&mut buf)
.expect("writing failed");
assert_eq!(size, 2);
assert_eq!(u16::from_be_bytes(buf[0..2].try_into().unwrap()), 5);
} }
#[test] #[test]

View File

@ -54,11 +54,11 @@ pub type GenericActionReplyPus = GenericMessage<ActionReplyPus>;
impl GenericActionReplyPus { impl GenericActionReplyPus {
pub fn new_action_reply( pub fn new_action_reply(
requestor_info: MessageMetadata, replier_info: MessageMetadata,
action_id: ActionId, action_id: ActionId,
reply: ActionReplyVariant, reply: ActionReplyVariant,
) -> Self { ) -> Self {
Self::new(requestor_info, ActionReplyPus::new(action_id, reply)) Self::new(replier_info, ActionReplyPus::new(action_id, reply))
} }
} }

View File

@ -331,10 +331,10 @@ mod tests {
fn severity_to_subservice(severity: Severity) -> Subservice { fn severity_to_subservice(severity: Severity) -> Subservice {
match severity { match severity {
Severity::INFO => Subservice::TmInfoReport, Severity::Info => Subservice::TmInfoReport,
Severity::LOW => Subservice::TmLowSeverityReport, Severity::Low => Subservice::TmLowSeverityReport,
Severity::MEDIUM => Subservice::TmMediumSeverityReport, Severity::Medium => Subservice::TmMediumSeverityReport,
Severity::HIGH => Subservice::TmHighSeverityReport, Severity::High => Subservice::TmHighSeverityReport,
} }
} }
@ -347,22 +347,22 @@ mod tests {
aux_data: Option<&[u8]>, aux_data: Option<&[u8]>,
) { ) {
match severity { match severity {
Severity::INFO => { Severity::Info => {
reporter reporter
.event_info(sender, time_stamp, event, aux_data) .event_info(sender, time_stamp, event, aux_data)
.expect("Error reporting info event"); .expect("Error reporting info event");
} }
Severity::LOW => { Severity::Low => {
reporter reporter
.event_low_severity(sender, time_stamp, event, aux_data) .event_low_severity(sender, time_stamp, event, aux_data)
.expect("Error reporting low event"); .expect("Error reporting low event");
} }
Severity::MEDIUM => { Severity::Medium => {
reporter reporter
.event_medium_severity(sender, time_stamp, event, aux_data) .event_medium_severity(sender, time_stamp, event, aux_data)
.expect("Error reporting medium event"); .expect("Error reporting medium event");
} }
Severity::HIGH => { Severity::High => {
reporter reporter
.event_high_severity(sender, time_stamp, event, aux_data) .event_high_severity(sender, time_stamp, event, aux_data)
.expect("Error reporting high event"); .expect("Error reporting high event");
@ -389,7 +389,7 @@ mod tests {
if let Some(err_data) = error_data { if let Some(err_data) = error_data {
error_copy.extend_from_slice(err_data); error_copy.extend_from_slice(err_data);
} }
let event = EventU32::new(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) let event = EventU32::new_checked(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
.expect("Error creating example event"); .expect("Error creating example event");
report_basic_event( report_basic_event(
&mut reporter, &mut reporter,
@ -407,7 +407,7 @@ mod tests {
severity_to_subservice(severity) as u8 severity_to_subservice(severity) as u8
); );
assert_eq!(tm_info.common.dest_id, 0); assert_eq!(tm_info.common.dest_id, 0);
assert_eq!(tm_info.common.time_stamp, time_stamp_empty); assert_eq!(tm_info.common.timestamp, time_stamp_empty);
assert_eq!(tm_info.common.msg_counter, 0); assert_eq!(tm_info.common.msg_counter, 0);
assert_eq!(tm_info.common.apid, EXAMPLE_APID); assert_eq!(tm_info.common.apid, EXAMPLE_APID);
assert_eq!(tm_info.event, event); assert_eq!(tm_info.event, event);
@ -417,35 +417,35 @@ mod tests {
#[test] #[test]
fn basic_info_event_generation() { fn basic_info_event_generation() {
basic_event_test(4, Severity::INFO, None); basic_event_test(4, Severity::Info, None);
} }
#[test] #[test]
fn basic_low_severity_event() { fn basic_low_severity_event() {
basic_event_test(4, Severity::LOW, None); basic_event_test(4, Severity::Low, None);
} }
#[test] #[test]
fn basic_medium_severity_event() { fn basic_medium_severity_event() {
basic_event_test(4, Severity::MEDIUM, None); basic_event_test(4, Severity::Medium, None);
} }
#[test] #[test]
fn basic_high_severity_event() { fn basic_high_severity_event() {
basic_event_test(4, Severity::HIGH, None); basic_event_test(4, Severity::High, None);
} }
#[test] #[test]
fn event_with_info_string() { fn event_with_info_string() {
let info_string = "Test Information"; let info_string = "Test Information";
basic_event_test(32, Severity::INFO, Some(info_string.as_bytes())); basic_event_test(32, Severity::Info, Some(info_string.as_bytes()));
} }
#[test] #[test]
fn low_severity_with_raw_err_data() { fn low_severity_with_raw_err_data() {
let raw_err_param: i32 = -1; let raw_err_param: i32 = -1;
let raw_err = raw_err_param.to_be_bytes(); let raw_err = raw_err_param.to_be_bytes();
basic_event_test(8, Severity::LOW, Some(&raw_err)) basic_event_test(8, Severity::Low, Some(&raw_err))
} }
fn check_buf_too_small( fn check_buf_too_small(
@ -454,7 +454,7 @@ mod tests {
expected_found_len: usize, expected_found_len: usize,
) { ) {
let time_stamp_empty: [u8; 7] = [0; 7]; let time_stamp_empty: [u8; 7] = [0; 7];
let event = EventU32::new(Severity::INFO, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) let event = EventU32::new_checked(Severity::Info, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
.expect("Error creating example event"); .expect("Error creating example event");
let err = reporter.event_info(sender, &time_stamp_empty, event, None); let err = reporter.event_info(sender, &time_stamp_empty, event, None);
assert!(err.is_err()); assert!(err.is_err());

View File

@ -28,7 +28,7 @@ pub use heapless_mod::*;
/// structure to track disabled events. A more primitive and embedded friendly /// structure to track disabled events. A more primitive and embedded friendly
/// solution could track this information in a static or pre-allocated list which contains /// solution could track this information in a static or pre-allocated list which contains
/// the disabled events. /// the disabled events.
pub trait PusEventMgmtBackendProvider<Event: GenericEvent> { pub trait PusEventReportingMapProvider<Event: GenericEvent> {
type Error; type Error;
fn event_enabled(&self, event: &Event) -> bool; fn event_enabled(&self, event: &Event) -> bool;
@ -56,7 +56,7 @@ pub mod heapless_mod {
{ {
} }
impl<const N: usize, Provider: GenericEvent> PusEventMgmtBackendProvider<Provider> impl<const N: usize, Provider: GenericEvent> PusEventReportingMapProvider<Provider>
for HeaplessPusMgmtBackendProvider<N, Provider> for HeaplessPusMgmtBackendProvider<N, Provider>
{ {
type Error = (); type Error = ();
@ -105,20 +105,24 @@ impl From<EcssTmtcError> for EventManError {
pub mod alloc_mod { pub mod alloc_mod {
use core::marker::PhantomData; use core::marker::PhantomData;
use crate::events::EventU16; use crate::{
events::EventU16,
params::{Params, WritableToBeBytes},
pus::event::{DummyEventHook, EventTmHookProvider},
};
use super::*; use super::*;
/// Default backend provider which uses a hash set as the event reporting status container /// Default backend provider which uses a hash set as the event reporting status container
/// like mentioned in the example of the [PusEventMgmtBackendProvider] documentation. /// like mentioned in the example of the [PusEventReportingMapProvider] documentation.
/// ///
/// This provider is a good option for host systems or larger embedded systems where /// This provider is a good option for host systems or larger embedded systems where
/// the expected occasional memory allocation performed by the [HashSet] is not an issue. /// the expected occasional memory allocation performed by the [HashSet] is not an issue.
pub struct DefaultPusEventMgmtBackend<Event: GenericEvent = EventU32> { pub struct DefaultPusEventReportingMap<Event: GenericEvent = EventU32> {
disabled: HashSet<Event>, disabled: HashSet<Event>,
} }
impl<Event: GenericEvent> Default for DefaultPusEventMgmtBackend<Event> { impl<Event: GenericEvent> Default for DefaultPusEventReportingMap<Event> {
fn default() -> Self { fn default() -> Self {
Self { Self {
disabled: HashSet::default(), disabled: HashSet::default(),
@ -126,51 +130,60 @@ pub mod alloc_mod {
} }
} }
impl<EV: GenericEvent + PartialEq + Eq + Hash + Copy + Clone> PusEventMgmtBackendProvider<EV> impl<Event: GenericEvent + PartialEq + Eq + Hash + Copy + Clone>
for DefaultPusEventMgmtBackend<EV> PusEventReportingMapProvider<Event> for DefaultPusEventReportingMap<Event>
{ {
type Error = (); type Error = ();
fn event_enabled(&self, event: &EV) -> bool { fn event_enabled(&self, event: &Event) -> bool {
!self.disabled.contains(event) !self.disabled.contains(event)
} }
fn enable_event_reporting(&mut self, event: &EV) -> Result<bool, Self::Error> { fn enable_event_reporting(&mut self, event: &Event) -> Result<bool, Self::Error> {
Ok(self.disabled.remove(event)) Ok(self.disabled.remove(event))
} }
fn disable_event_reporting(&mut self, event: &EV) -> Result<bool, Self::Error> { fn disable_event_reporting(&mut self, event: &Event) -> Result<bool, Self::Error> {
Ok(self.disabled.insert(*event)) Ok(self.disabled.insert(*event))
} }
} }
pub struct PusEventDispatcher< #[derive(Debug, Copy, Clone, PartialEq, Eq)]
B: PusEventMgmtBackendProvider<EV, Error = E>, pub struct EventGenerationResult {
EV: GenericEvent, pub event_was_enabled: bool,
E, pub params_were_propagated: bool,
> {
reporter: EventReporter,
backend: B,
phantom: PhantomData<(E, EV)>,
} }
impl<B: PusEventMgmtBackendProvider<Event, Error = E>, Event: GenericEvent, E> pub struct PusEventTmCreatorWithMap<
PusEventDispatcher<B, Event, E> ReportingMap: PusEventReportingMapProvider<Event>,
Event: GenericEvent,
EventTmHook: EventTmHookProvider = DummyEventHook,
> {
pub reporter: EventReporter<EventTmHook>,
reporting_map: ReportingMap,
phantom: PhantomData<Event>,
}
impl<
ReportingMap: PusEventReportingMapProvider<Event>,
Event: GenericEvent,
EventTmHook: EventTmHookProvider,
> PusEventTmCreatorWithMap<ReportingMap, Event, EventTmHook>
{ {
pub fn new(reporter: EventReporter, backend: B) -> Self { pub fn new(reporter: EventReporter<EventTmHook>, backend: ReportingMap) -> Self {
Self { Self {
reporter, reporter,
backend, reporting_map: backend,
phantom: PhantomData, phantom: PhantomData,
} }
} }
pub fn enable_tm_for_event(&mut self, event: &Event) -> Result<bool, E> { pub fn enable_tm_for_event(&mut self, event: &Event) -> Result<bool, ReportingMap::Error> {
self.backend.enable_event_reporting(event) self.reporting_map.enable_event_reporting(event)
} }
pub fn disable_tm_for_event(&mut self, event: &Event) -> Result<bool, E> { pub fn disable_tm_for_event(&mut self, event: &Event) -> Result<bool, ReportingMap::Error> {
self.backend.disable_event_reporting(event) self.reporting_map.disable_event_reporting(event)
} }
pub fn generate_pus_event_tm_generic( pub fn generate_pus_event_tm_generic(
@ -180,59 +193,108 @@ pub mod alloc_mod {
event: Event, event: Event,
params: Option<&[u8]>, params: Option<&[u8]>,
) -> Result<bool, EventManError> { ) -> Result<bool, EventManError> {
if !self.backend.event_enabled(&event) { if !self.reporting_map.event_enabled(&event) {
return Ok(false); return Ok(false);
} }
match event.severity() { match event.severity() {
Severity::INFO => self Severity::Info => self
.reporter .reporter
.event_info(sender, time_stamp, event, params) .event_info(sender, time_stamp, event, params)
.map(|_| true) .map(|_| true)
.map_err(|e| e.into()), .map_err(|e| e.into()),
Severity::LOW => self Severity::Low => self
.reporter .reporter
.event_low_severity(sender, time_stamp, event, params) .event_low_severity(sender, time_stamp, event, params)
.map(|_| true) .map(|_| true)
.map_err(|e| e.into()), .map_err(|e| e.into()),
Severity::MEDIUM => self Severity::Medium => self
.reporter .reporter
.event_medium_severity(sender, time_stamp, event, params) .event_medium_severity(sender, time_stamp, event, params)
.map(|_| true) .map(|_| true)
.map_err(|e| e.into()), .map_err(|e| e.into()),
Severity::HIGH => self Severity::High => self
.reporter .reporter
.event_high_severity(sender, time_stamp, event, params) .event_high_severity(sender, time_stamp, event, params)
.map(|_| true) .map(|_| true)
.map_err(|e| e.into()), .map_err(|e| e.into()),
} }
} }
pub fn generate_pus_event_tm_generic_with_generic_params(
&self,
sender: &(impl EcssTmSender + ?Sized),
time_stamp: &[u8],
event: Event,
small_data_buf: &mut [u8],
params: Option<&Params>,
) -> Result<EventGenerationResult, EventManError> {
let mut result = EventGenerationResult {
event_was_enabled: false,
params_were_propagated: true,
};
if params.is_none() {
result.event_was_enabled =
self.generate_pus_event_tm_generic(sender, time_stamp, event, None)?;
return Ok(result);
}
let params = params.unwrap();
result.event_was_enabled = match params {
Params::Heapless(heapless_param) => {
heapless_param
.write_to_be_bytes(&mut small_data_buf[..heapless_param.written_len()])
.map_err(EcssTmtcError::ByteConversion)?;
self.generate_pus_event_tm_generic(
sender,
time_stamp,
event,
Some(small_data_buf),
)?
}
Params::Vec(vec) => {
self.generate_pus_event_tm_generic(sender, time_stamp, event, Some(vec))?
}
Params::String(string) => self.generate_pus_event_tm_generic(
sender,
time_stamp,
event,
Some(string.as_bytes()),
)?,
_ => {
result.params_were_propagated = false;
self.generate_pus_event_tm_generic(sender, time_stamp, event, None)?
}
};
Ok(result)
}
} }
impl<EV: GenericEvent + Copy + PartialEq + Eq + Hash> impl<Event: GenericEvent + Copy + PartialEq + Eq + Hash, EventTmHook: EventTmHookProvider>
PusEventDispatcher<DefaultPusEventMgmtBackend<EV>, EV, ()> PusEventTmCreatorWithMap<DefaultPusEventReportingMap<Event>, Event, EventTmHook>
{ {
pub fn new_with_default_backend(reporter: EventReporter) -> Self { pub fn new_with_default_backend(reporter: EventReporter<EventTmHook>) -> Self {
Self { Self {
reporter, reporter,
backend: DefaultPusEventMgmtBackend::default(), reporting_map: DefaultPusEventReportingMap::default(),
phantom: PhantomData, phantom: PhantomData,
} }
} }
} }
impl<B: PusEventMgmtBackendProvider<EventU32, Error = E>, E> PusEventDispatcher<B, EventU32, E> { impl<ReportingMap: PusEventReportingMapProvider<EventU32>>
PusEventTmCreatorWithMap<ReportingMap, EventU32>
{
pub fn enable_tm_for_event_with_sev<Severity: HasSeverity>( pub fn enable_tm_for_event_with_sev<Severity: HasSeverity>(
&mut self, &mut self,
event: &EventU32TypedSev<Severity>, event: &EventU32TypedSev<Severity>,
) -> Result<bool, E> { ) -> Result<bool, ReportingMap::Error> {
self.backend.enable_event_reporting(event.as_ref()) self.reporting_map.enable_event_reporting(event.as_ref())
} }
pub fn disable_tm_for_event_with_sev<Severity: HasSeverity>( pub fn disable_tm_for_event_with_sev<Severity: HasSeverity>(
&mut self, &mut self,
event: &EventU32TypedSev<Severity>, event: &EventU32TypedSev<Severity>,
) -> Result<bool, E> { ) -> Result<bool, ReportingMap::Error> {
self.backend.disable_event_reporting(event.as_ref()) self.reporting_map.disable_event_reporting(event.as_ref())
} }
pub fn generate_pus_event_tm<Severity: HasSeverity>( pub fn generate_pus_event_tm<Severity: HasSeverity>(
@ -246,35 +308,40 @@ pub mod alloc_mod {
} }
} }
pub type DefaultPusEventU16Dispatcher<E> = pub type DefaultPusEventU16TmCreator<EventTmHook = DummyEventHook> =
PusEventDispatcher<DefaultPusEventMgmtBackend<EventU16>, EventU16, E>; PusEventTmCreatorWithMap<DefaultPusEventReportingMap<EventU16>, EventU16, EventTmHook>;
pub type DefaultPusEventU32Dispatcher<E> = pub type DefaultPusEventU32TmCreator<EventTmHook = DummyEventHook> =
PusEventDispatcher<DefaultPusEventMgmtBackend<EventU32>, EventU32, E>; PusEventTmCreatorWithMap<DefaultPusEventReportingMap<EventU32>, EventU32, EventTmHook>;
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use alloc::string::{String, ToString};
use alloc::vec;
use spacepackets::ecss::event::Subservice;
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::PusPacket;
use super::*; use super::*;
use crate::request::UniqueApidTargetId; use crate::request::UniqueApidTargetId;
use crate::{events::SeverityInfo, tmtc::PacketAsVec}; use crate::{events::SeverityInfo, tmtc::PacketAsVec};
use std::sync::mpsc::{self, TryRecvError}; use std::sync::mpsc::{self, TryRecvError};
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(1, 0);
EventU32TypedSev::<SeverityInfo>::const_new(1, 0); const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5);
const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5);
const EMPTY_STAMP: [u8; 7] = [0; 7]; const EMPTY_STAMP: [u8; 7] = [0; 7];
const TEST_APID: u16 = 0x02; const TEST_APID: u16 = 0x02;
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
fn create_basic_man_1() -> DefaultPusEventU32Dispatcher<()> { fn create_basic_man_1() -> DefaultPusEventU32TmCreator {
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128) let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
.expect("Creating event repoter failed"); .expect("Creating event repoter failed");
PusEventDispatcher::new_with_default_backend(reporter) PusEventTmCreatorWithMap::new_with_default_backend(reporter)
} }
fn create_basic_man_2() -> DefaultPusEventU32Dispatcher<()> { fn create_basic_man_2() -> DefaultPusEventU32TmCreator {
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128) let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
.expect("Creating event repoter failed"); .expect("Creating event repoter failed");
let backend = DefaultPusEventMgmtBackend::default(); let backend = DefaultPusEventReportingMap::default();
PusEventDispatcher::new(reporter, backend) PusEventTmCreatorWithMap::new(reporter, backend)
} }
#[test] #[test]
@ -329,4 +396,70 @@ mod tests {
assert!(event_sent); assert!(event_sent);
event_rx.try_recv().expect("No info event received"); event_rx.try_recv().expect("No info event received");
} }
#[test]
fn test_event_with_generic_string_param() {
let event_man = create_basic_man_1();
let mut small_data_buf = [0; 128];
let param_data = "hello world";
let (event_tx, event_rx) = mpsc::channel::<PacketAsVec>();
let res = event_man.generate_pus_event_tm_generic_with_generic_params(
&event_tx,
&EMPTY_STAMP,
INFO_EVENT.into(),
&mut small_data_buf,
Some(&param_data.to_string().into()),
);
assert!(res.is_ok());
let res = res.unwrap();
assert!(res.event_was_enabled);
assert!(res.params_were_propagated);
let event_tm = event_rx.try_recv().expect("no event received");
let (tm, _) = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed");
assert_eq!(tm.service(), 5);
assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8);
assert_eq!(tm.user_data().len(), 4 + param_data.len());
let u32_event = u32::from_be_bytes(tm.user_data()[0..4].try_into().unwrap());
assert_eq!(u32_event, INFO_EVENT.raw());
let string_data = String::from_utf8_lossy(&tm.user_data()[4..]);
assert_eq!(string_data, param_data);
}
#[test]
fn test_event_with_generic_vec_param() {
let event_man = create_basic_man_1();
let mut small_data_buf = [0; 128];
let param_data = vec![1, 2, 3, 4];
let (event_tx, event_rx) = mpsc::channel::<PacketAsVec>();
let res = event_man.generate_pus_event_tm_generic_with_generic_params(
&event_tx,
&EMPTY_STAMP,
INFO_EVENT.into(),
&mut small_data_buf,
Some(&param_data.clone().into()),
);
assert!(res.is_ok());
let res = res.unwrap();
assert!(res.event_was_enabled);
assert!(res.params_were_propagated);
let event_tm = event_rx.try_recv().expect("no event received");
let (tm, _) = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed");
assert_eq!(tm.service(), 5);
assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8);
assert_eq!(tm.user_data().len(), 4 + param_data.len());
let u32_event = u32::from_be_bytes(tm.user_data()[0..4].try_into().unwrap());
assert_eq!(u32_event, INFO_EVENT.raw());
let vec_data = tm.user_data()[4..].to_vec();
assert_eq!(vec_data, param_data);
}
#[test]
fn test_event_with_generic_store_param_not_propagated() {
// TODO: Test this.
}
#[test]
fn test_event_with_generic_heapless_param() {
// TODO: Test this.
}
} }

View File

@ -1,7 +1,7 @@
use crate::events::EventU32; use crate::events::EventU32;
use crate::pus::event_man::{EventRequest, EventRequestWithToken}; use crate::pus::event_man::{EventRequest, EventRequestWithToken};
use crate::pus::verification::TcStateToken; use crate::pus::verification::TcStateToken;
use crate::pus::{PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError}; use crate::pus::{DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError};
use crate::queue::GenericSendError; use crate::queue::GenericSendError;
use spacepackets::ecss::event::Subservice; use spacepackets::ecss::event::Subservice;
use spacepackets::ecss::PusPacket; use spacepackets::ecss::PusPacket;
@ -10,7 +10,7 @@ use std::sync::mpsc::Sender;
use super::verification::VerificationReportingProvider; use super::verification::VerificationReportingProvider;
use super::{ use super::{
EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, GenericConversionError, EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, GenericConversionError,
GenericRoutingError, PusServiceHelper, GenericRoutingError, HandlingStatus, PusServiceHelper,
}; };
pub struct PusEventServiceHandler< pub struct PusEventServiceHandler<
@ -46,13 +46,14 @@ impl<
} }
} }
pub fn poll_and_handle_next_tc( pub fn poll_and_handle_next_tc<ErrorCb: FnMut(&PartialPusHandlingError)>(
&mut self, &mut self,
mut error_callback: ErrorCb,
time_stamp: &[u8], time_stamp: &[u8],
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { ) -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() { if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty); return Ok(HandlingStatus::Empty.into());
} }
let ecss_tc_and_token = possible_packet.unwrap(); let ecss_tc_and_token = possible_packet.unwrap();
self.service_helper self.service_helper
@ -62,13 +63,13 @@ impl<
let subservice = tc.subservice(); let subservice = tc.subservice();
let srv = Subservice::try_from(subservice); let srv = Subservice::try_from(subservice);
if srv.is_err() { if srv.is_err() {
return Ok(PusPacketHandlerResult::CustomSubservice( return Ok(DirectPusPacketHandlerResult::CustomSubservice(
tc.subservice(), tc.subservice(),
ecss_tc_and_token.token, ecss_tc_and_token.token,
)); ));
} }
let handle_enable_disable_request = let mut handle_enable_disable_request =
|enable: bool| -> Result<PusPacketHandlerResult, PusPacketHandlingError> { |enable: bool| -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError> {
if tc.user_data().len() < 4 { if tc.user_data().len() < 4 {
return Err(GenericConversionError::NotEnoughAppData { return Err(GenericConversionError::NotEnoughAppData {
expected: 4, expected: 4,
@ -79,21 +80,20 @@ impl<
let user_data = tc.user_data(); let user_data = tc.user_data();
let event_u32 = let event_u32 =
EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap())); EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap()));
let start_token = self
.service_helper
.common
.verif_reporter
.start_success(
&self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
time_stamp,
)
.map_err(|_| PartialPusHandlingError::Verification);
let partial_error = start_token.clone().err();
let mut token: TcStateToken = ecss_tc_and_token.token.into(); let mut token: TcStateToken = ecss_tc_and_token.token.into();
if let Ok(start_token) = start_token { match self.service_helper.common.verif_reporter.start_success(
token = start_token.into(); &self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
time_stamp,
) {
Ok(start_token) => {
token = start_token.into();
}
Err(e) => {
error_callback(&PartialPusHandlingError::Verification(e));
}
} }
let event_req_with_token = if enable { let event_req_with_token = if enable {
EventRequestWithToken { EventRequestWithToken {
request: EventRequest::Enable(event_u32), request: EventRequest::Enable(event_u32),
@ -112,12 +112,7 @@ impl<
GenericSendError::RxDisconnected, GenericSendError::RxDisconnected,
)) ))
})?; })?;
if let Some(partial_error) = partial_error { Ok(HandlingStatus::HandledOne.into())
return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
partial_error,
));
}
Ok(PusPacketHandlerResult::RequestHandled)
}; };
match srv.unwrap() { match srv.unwrap() {
@ -136,14 +131,14 @@ impl<
handle_enable_disable_request(false)?; handle_enable_disable_request(false)?;
} }
Subservice::TcReportDisabledList | Subservice::TmDisabledEventsReport => { Subservice::TcReportDisabledList | Subservice::TmDisabledEventsReport => {
return Ok(PusPacketHandlerResult::SubserviceNotImplemented( return Ok(DirectPusPacketHandlerResult::SubserviceNotImplemented(
subservice, subservice,
ecss_tc_and_token.token, ecss_tc_and_token.token,
)); ));
} }
} }
Ok(PusPacketHandlerResult::RequestHandled) Ok(HandlingStatus::HandledOne.into())
} }
} }
@ -167,7 +162,7 @@ mod tests {
use crate::pus::verification::{ use crate::pus::verification::{
RequestId, VerificationReporter, VerificationReportingProvider, RequestId, VerificationReporter, VerificationReportingProvider,
}; };
use crate::pus::{GenericConversionError, MpscTcReceiver}; use crate::pus::{GenericConversionError, HandlingStatus, MpscTcReceiver};
use crate::tmtc::PacketSenderWithSharedPool; use crate::tmtc::PacketSenderWithSharedPool;
use crate::{ use crate::{
events::EventU32, events::EventU32,
@ -175,13 +170,13 @@ mod tests {
event_man::EventRequestWithToken, event_man::EventRequestWithToken,
tests::PusServiceHandlerWithSharedStoreCommon, tests::PusServiceHandlerWithSharedStoreCommon,
verification::{TcStateAccepted, VerificationToken}, verification::{TcStateAccepted, VerificationToken},
EcssTcInSharedStoreConverter, PusPacketHandlerResult, PusPacketHandlingError, DirectPusPacketHandlerResult, EcssTcInSharedStoreConverter, PusPacketHandlingError,
}, },
}; };
use super::PusEventServiceHandler; use super::PusEventServiceHandler;
const TEST_EVENT_0: EventU32 = EventU32::const_new(crate::events::Severity::INFO, 5, 25); const TEST_EVENT_0: EventU32 = EventU32::new(crate::events::Severity::Info, 5, 25);
struct Pus5HandlerWithStoreTester { struct Pus5HandlerWithStoreTester {
common: PusServiceHandlerWithSharedStoreCommon, common: PusServiceHandlerWithSharedStoreCommon,
@ -229,9 +224,11 @@ mod tests {
} }
impl SimplePusPacketHandler for Pus5HandlerWithStoreTester { impl SimplePusPacketHandler for Pus5HandlerWithStoreTester {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { fn handle_one_tc(
&mut self,
) -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.poll_and_handle_next_tc(&time_stamp) self.handler.poll_and_handle_next_tc(|_| {}, &time_stamp)
} }
} }
@ -293,10 +290,13 @@ mod tests {
let result = test_harness.handle_one_tc(); let result = test_harness.handle_one_tc();
assert!(result.is_ok()); assert!(result.is_ok());
let result = result.unwrap(); let result = result.unwrap();
if let PusPacketHandlerResult::Empty = result { assert!(
} else { matches!(
panic!("unexpected result type {result:?}") result,
} DirectPusPacketHandlerResult::Handled(HandlingStatus::Empty)
),
"unexpected result type {result:?}"
)
} }
#[test] #[test]
@ -311,7 +311,7 @@ mod tests {
let result = test_harness.handle_one_tc(); let result = test_harness.handle_one_tc();
assert!(result.is_ok()); assert!(result.is_ok());
let result = result.unwrap(); let result = result.unwrap();
if let PusPacketHandlerResult::CustomSubservice(subservice, _) = result { if let DirectPusPacketHandlerResult::CustomSubservice(subservice, _) = result {
assert_eq!(subservice, 200); assert_eq!(subservice, 200);
} else { } else {
panic!("unexpected result type {result:?}") panic!("unexpected result type {result:?}")

View File

@ -45,6 +45,15 @@ pub use std_mod::*;
use self::verification::VerificationReportingProvider; use self::verification::VerificationReportingProvider;
/// Generic handling status for an object which is able to continuosly handle a queue to handle
/// request or replies until the queue is empty.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum HandlingStatus {
HandledOne,
Empty,
}
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum PusTmVariant<'time, 'src_data> { pub enum PusTmVariant<'time, 'src_data> {
InStore(PoolAddr), InStore(PoolAddr),
@ -649,14 +658,11 @@ pub mod alloc_mod {
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub mod std_mod { pub mod std_mod {
use super::*;
use crate::pool::{ use crate::pool::{
PoolAddr, PoolError, PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool, PoolAddr, PoolError, PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool,
}; };
use crate::pus::verification::{TcStateAccepted, VerificationToken}; use crate::pus::verification::{TcStateAccepted, VerificationToken};
use crate::pus::{
EcssTcAndToken, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericReceiveError,
GenericSendError, PusTmVariant, TryRecvTmtcError,
};
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use crate::ComponentId; use crate::ComponentId;
use alloc::vec::Vec; use alloc::vec::Vec;
@ -920,26 +926,24 @@ pub mod std_mod {
#[error("generic timestamp generation error")] #[error("generic timestamp generation error")]
Time(#[from] StdTimestampError), Time(#[from] StdTimestampError),
#[error("error sending telemetry: {0}")] #[error("error sending telemetry: {0}")]
TmSend(#[from] EcssTmtcError), TmSend(EcssTmtcError),
#[error("error sending verification message")] #[error("error sending verification message")]
Verification, Verification(EcssTmtcError),
#[error("invalid verification token")] #[error("invalid verification token")]
NoVerificationToken, NoVerificationToken,
} }
/// Generic result type for handlers which can process PUS packets. /// Generic result type for handlers which can process PUS packets.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum PusPacketHandlerResult { pub enum DirectPusPacketHandlerResult {
RequestHandled, Handled(HandlingStatus),
RequestHandledPartialSuccess(PartialPusHandlingError),
SubserviceNotImplemented(u8, VerificationToken<TcStateAccepted>), SubserviceNotImplemented(u8, VerificationToken<TcStateAccepted>),
CustomSubservice(u8, VerificationToken<TcStateAccepted>), CustomSubservice(u8, VerificationToken<TcStateAccepted>),
Empty,
} }
impl From<PartialPusHandlingError> for PusPacketHandlerResult { impl From<HandlingStatus> for DirectPusPacketHandlerResult {
fn from(value: PartialPusHandlingError) -> Self { fn from(value: HandlingStatus) -> Self {
Self::RequestHandledPartialSuccess(value) Self::Handled(value)
} }
} }
@ -1222,7 +1226,7 @@ pub mod test_util {
use super::{ use super::{
verification::{self, TcStateAccepted, VerificationToken}, verification::{self, TcStateAccepted, VerificationToken},
PusPacketHandlerResult, PusPacketHandlingError, DirectPusPacketHandlerResult, PusPacketHandlingError,
}; };
pub const TEST_APID: u16 = 0x101; pub const TEST_APID: u16 = 0x101;
@ -1246,7 +1250,8 @@ pub mod test_util {
} }
pub trait SimplePusPacketHandler { pub trait SimplePusPacketHandler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>; fn handle_one_tc(&mut self)
-> Result<DirectPusPacketHandlerResult, PusPacketHandlingError>;
} }
} }
@ -1284,36 +1289,46 @@ pub mod tests {
pub seq_count: u16, pub seq_count: u16,
pub msg_counter: u16, pub msg_counter: u16,
pub dest_id: u16, pub dest_id: u16,
pub time_stamp: [u8; 7], pub timestamp: Vec<u8>,
} }
impl CommonTmInfo { impl CommonTmInfo {
pub fn new_zero_seq_count( pub fn new(
subservice: u8, subservice: u8,
apid: u16, apid: u16,
seq_count: u16,
msg_counter: u16,
dest_id: u16, dest_id: u16,
time_stamp: [u8; 7], timestamp: &[u8],
) -> Self { ) -> Self {
Self { Self {
subservice, subservice,
apid, apid,
seq_count: 0, seq_count,
msg_counter: 0, msg_counter,
dest_id, dest_id,
time_stamp, timestamp: timestamp.to_vec(),
} }
} }
pub fn new_zero_seq_count(
subservice: u8,
apid: u16,
dest_id: u16,
timestamp: &[u8],
) -> Self {
Self::new(subservice, apid, 0, 0, dest_id, timestamp)
}
pub fn new_from_tm(tm: &PusTmCreator) -> Self { pub fn new_from_tm(tm: &PusTmCreator) -> Self {
let mut time_stamp = [0; 7]; let mut timestamp = [0; 7];
time_stamp.clone_from_slice(&tm.timestamp()[0..7]); timestamp.clone_from_slice(&tm.timestamp()[0..7]);
Self { Self {
subservice: PusPacket::subservice(tm), subservice: PusPacket::subservice(tm),
apid: tm.apid(), apid: tm.apid(),
seq_count: tm.seq_count(), seq_count: tm.seq_count(),
msg_counter: tm.msg_counter(), msg_counter: tm.msg_counter(),
dest_id: tm.dest_id(), dest_id: tm.dest_id(),
time_stamp, timestamp: timestamp.to_vec(),
} }
} }
} }

View File

@ -1,11 +1,12 @@
use super::scheduler::PusSchedulerProvider; use super::scheduler::PusSchedulerProvider;
use super::verification::{VerificationReporter, VerificationReportingProvider}; use super::verification::{VerificationReporter, VerificationReportingProvider};
use super::{ use super::{
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiver, DirectPusPacketHandlerResult, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
EcssTmSender, MpscTcReceiver, PusServiceHelper, EcssTcInVecConverter, EcssTcReceiver, EcssTmSender, HandlingStatus, MpscTcReceiver,
PartialPusHandlingError, PusServiceHelper,
}; };
use crate::pool::PoolProvider; use crate::pool::PoolProvider;
use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError}; use crate::pus::PusPacketHandlingError;
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use alloc::string::ToString; use alloc::string::ToString;
use spacepackets::ecss::{scheduling, PusPacket}; use spacepackets::ecss::{scheduling, PusPacket};
@ -64,14 +65,15 @@ impl<
&self.scheduler &self.scheduler
} }
pub fn poll_and_handle_next_tc( pub fn poll_and_handle_next_tc<ErrorCb: FnMut(&PartialPusHandlingError)>(
&mut self, &mut self,
mut error_callback: ErrorCb,
time_stamp: &[u8], time_stamp: &[u8],
sched_tc_pool: &mut (impl PoolProvider + ?Sized), sched_tc_pool: &mut (impl PoolProvider + ?Sized),
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { ) -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() { if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty); return Ok(HandlingStatus::Empty.into());
} }
let ecss_tc_and_token = possible_packet.unwrap(); let ecss_tc_and_token = possible_packet.unwrap();
self.service_helper self.service_helper
@ -81,34 +83,34 @@ impl<
let subservice = PusPacket::subservice(&tc); let subservice = PusPacket::subservice(&tc);
let standard_subservice = scheduling::Subservice::try_from(subservice); let standard_subservice = scheduling::Subservice::try_from(subservice);
if standard_subservice.is_err() { if standard_subservice.is_err() {
return Ok(PusPacketHandlerResult::CustomSubservice( return Ok(DirectPusPacketHandlerResult::CustomSubservice(
subservice, subservice,
ecss_tc_and_token.token, ecss_tc_and_token.token,
)); ));
} }
let partial_error = None;
match standard_subservice.unwrap() { match standard_subservice.unwrap() {
scheduling::Subservice::TcEnableScheduling => { scheduling::Subservice::TcEnableScheduling => {
let start_token = self let opt_started_token = match self.service_helper.verif_reporter().start_success(
.service_helper &self.service_helper.common.tm_sender,
.verif_reporter() ecss_tc_and_token.token,
.start_success( time_stamp,
&self.service_helper.common.tm_sender, ) {
ecss_tc_and_token.token, Ok(started_token) => Some(started_token),
time_stamp, Err(e) => {
) error_callback(&PartialPusHandlingError::Verification(e));
.expect("Error sending start success"); None
}
};
self.scheduler.enable(); self.scheduler.enable();
if self.scheduler.is_enabled() {
self.service_helper if self.scheduler.is_enabled() && opt_started_token.is_some() {
.verif_reporter() if let Err(e) = self.service_helper.verif_reporter().completion_success(
.completion_success( &self.service_helper.common.tm_sender,
&self.service_helper.common.tm_sender, opt_started_token.unwrap(),
start_token, time_stamp,
time_stamp, ) {
) error_callback(&PartialPusHandlingError::Verification(e));
.expect("Error sending completion success"); }
} else { } else {
return Err(PusPacketHandlingError::Other( return Err(PusPacketHandlingError::Other(
"failed to enabled scheduler".to_string(), "failed to enabled scheduler".to_string(),
@ -116,26 +118,27 @@ impl<
} }
} }
scheduling::Subservice::TcDisableScheduling => { scheduling::Subservice::TcDisableScheduling => {
let start_token = self let opt_started_token = match self.service_helper.verif_reporter().start_success(
.service_helper &self.service_helper.common.tm_sender,
.verif_reporter() ecss_tc_and_token.token,
.start_success( time_stamp,
&self.service_helper.common.tm_sender, ) {
ecss_tc_and_token.token, Ok(started_token) => Some(started_token),
time_stamp, Err(e) => {
) error_callback(&PartialPusHandlingError::Verification(e));
.expect("Error sending start success"); None
}
};
self.scheduler.disable(); self.scheduler.disable();
if !self.scheduler.is_enabled() { if !self.scheduler.is_enabled() && opt_started_token.is_some() {
self.service_helper if let Err(e) = self.service_helper.verif_reporter().completion_success(
.verif_reporter() &self.service_helper.common.tm_sender,
.completion_success( opt_started_token.unwrap(),
&self.service_helper.common.tm_sender, time_stamp,
start_token, ) {
time_stamp, error_callback(&PartialPusHandlingError::Verification(e));
) }
.expect("Error sending completion success");
} else { } else {
return Err(PusPacketHandlingError::Other( return Err(PusPacketHandlingError::Other(
"failed to disable scheduler".to_string(), "failed to disable scheduler".to_string(),
@ -194,18 +197,13 @@ impl<
} }
_ => { _ => {
// Treat unhandled standard subservices as custom subservices for now. // Treat unhandled standard subservices as custom subservices for now.
return Ok(PusPacketHandlerResult::CustomSubservice( return Ok(DirectPusPacketHandlerResult::CustomSubservice(
subservice, subservice,
ecss_tc_and_token.token, ecss_tc_and_token.token,
)); ));
} }
} }
if let Some(partial_error) = partial_error { Ok(HandlingStatus::HandledOne.into())
return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
partial_error,
));
}
Ok(PusPacketHandlerResult::RequestHandled)
} }
} }
/// Helper type definition for a PUS 11 handler with a dynamic TMTC memory backend and regular /// Helper type definition for a PUS 11 handler with a dynamic TMTC memory backend and regular
@ -257,7 +255,7 @@ mod tests {
verification::{RequestId, TcStateAccepted, VerificationToken}, verification::{RequestId, TcStateAccepted, VerificationToken},
EcssTcInSharedStoreConverter, EcssTcInSharedStoreConverter,
}; };
use crate::pus::{MpscTcReceiver, PusPacketHandlerResult, PusPacketHandlingError}; use crate::pus::{DirectPusPacketHandlerResult, MpscTcReceiver, PusPacketHandlingError};
use crate::tmtc::PacketSenderWithSharedPool; use crate::tmtc::PacketSenderWithSharedPool;
use alloc::collections::VecDeque; use alloc::collections::VecDeque;
use delegate::delegate; use delegate::delegate;
@ -298,10 +296,12 @@ mod tests {
} }
} }
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { pub fn handle_one_tc(
&mut self,
) -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler self.handler
.poll_and_handle_next_tc(&time_stamp, &mut self.sched_tc_pool) .poll_and_handle_next_tc(|_| {}, &time_stamp, &mut self.sched_tc_pool)
} }
} }
@ -387,7 +387,7 @@ mod tests {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
test_harness test_harness
.handler .handler
.poll_and_handle_next_tc(&time_stamp, &mut test_harness.sched_tc_pool) .poll_and_handle_next_tc(|_| {}, &time_stamp, &mut test_harness.sched_tc_pool)
.unwrap(); .unwrap();
test_harness.check_next_verification_tm(1, request_id); test_harness.check_next_verification_tm(1, request_id);
test_harness.check_next_verification_tm(3, request_id); test_harness.check_next_verification_tm(3, request_id);

View File

@ -1,5 +1,5 @@
use crate::pus::{ use crate::pus::{
PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusTmVariant, DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError, PusTmVariant,
}; };
use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader}; use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
@ -10,7 +10,7 @@ use std::sync::mpsc;
use super::verification::{VerificationReporter, VerificationReportingProvider}; use super::verification::{VerificationReporter, VerificationReportingProvider};
use super::{ use super::{
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiver, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiver,
EcssTmSender, GenericConversionError, MpscTcReceiver, PusServiceHelper, EcssTmSender, GenericConversionError, HandlingStatus, MpscTcReceiver, PusServiceHelper,
}; };
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets. /// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
@ -43,13 +43,14 @@ impl<
Self { service_helper } Self { service_helper }
} }
pub fn poll_and_handle_next_tc( pub fn poll_and_handle_next_tc<ErrorCb: FnMut(&PartialPusHandlingError)>(
&mut self, &mut self,
mut error_callback: ErrorCb,
time_stamp: &[u8], time_stamp: &[u8],
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { ) -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() { if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty); return Ok(HandlingStatus::Empty.into());
} }
let ecss_tc_and_token = possible_packet.unwrap(); let ecss_tc_and_token = possible_packet.unwrap();
self.service_helper self.service_helper
@ -60,21 +61,16 @@ impl<
return Err(GenericConversionError::WrongService(tc.service()).into()); return Err(GenericConversionError::WrongService(tc.service()).into());
} }
if tc.subservice() == 1 { if tc.subservice() == 1 {
let mut partial_error = None; let opt_started_token = match self.service_helper.verif_reporter().start_success(
let result = self &self.service_helper.common.tm_sender,
.service_helper ecss_tc_and_token.token,
.verif_reporter() time_stamp,
.start_success( ) {
&self.service_helper.common.tm_sender, Ok(token) => Some(token),
ecss_tc_and_token.token, Err(e) => {
time_stamp, error_callback(&PartialPusHandlingError::Verification(e));
) None
.map_err(|_| PartialPusHandlingError::Verification); }
let start_token = if let Ok(result) = result {
Some(result)
} else {
partial_error = Some(result.unwrap_err());
None
}; };
// Sequence count will be handled centrally in TM funnel. // 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 // It is assumed that the verification reporter was built with a valid APID, so we use
@ -83,42 +79,30 @@ impl<
SpHeader::new_for_unseg_tm(self.service_helper.verif_reporter().apid(), 0, 0); SpHeader::new_for_unseg_tm(self.service_helper.verif_reporter().apid(), 0, 0);
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp); 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, &[], true);
let result = self if let Err(e) = self
.service_helper .service_helper
.common .common
.tm_sender .tm_sender
.send_tm(self.service_helper.id(), PusTmVariant::Direct(ping_reply)) .send_tm(self.service_helper.id(), PusTmVariant::Direct(ping_reply))
.map_err(PartialPusHandlingError::TmSend); {
if let Err(err) = result { error_callback(&PartialPusHandlingError::TmSend(e));
partial_error = Some(err);
} }
if let Some(start_token) = opt_started_token {
if let Some(start_token) = start_token { if let Err(e) = self.service_helper.verif_reporter().completion_success(
if self &self.service_helper.common.tm_sender,
.service_helper start_token,
.verif_reporter() time_stamp,
.completion_success( ) {
&self.service_helper.common.tm_sender, error_callback(&PartialPusHandlingError::Verification(e));
start_token,
time_stamp,
)
.is_err()
{
partial_error = Some(PartialPusHandlingError::Verification)
} }
} }
if let Some(partial_error) = partial_error {
return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
partial_error,
));
};
} else { } else {
return Ok(PusPacketHandlerResult::CustomSubservice( return Ok(DirectPusPacketHandlerResult::CustomSubservice(
tc.subservice(), tc.subservice(),
ecss_tc_and_token.token, ecss_tc_and_token.token,
)); ));
} }
Ok(PusPacketHandlerResult::RequestHandled) Ok(HandlingStatus::HandledOne.into())
} }
} }
@ -158,8 +142,9 @@ mod tests {
}; };
use crate::pus::verification::{TcStateAccepted, VerificationToken}; use crate::pus::verification::{TcStateAccepted, VerificationToken};
use crate::pus::{ use crate::pus::{
EcssTcInSharedStoreConverter, EcssTcInVecConverter, GenericConversionError, MpscTcReceiver, DirectPusPacketHandlerResult, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
MpscTmAsVecSender, PusPacketHandlerResult, PusPacketHandlingError, GenericConversionError, HandlingStatus, MpscTcReceiver, MpscTmAsVecSender,
PartialPusHandlingError, PusPacketHandlingError,
}; };
use crate::tmtc::PacketSenderWithSharedPool; use crate::tmtc::PacketSenderWithSharedPool;
use crate::ComponentId; use crate::ComponentId;
@ -221,9 +206,12 @@ mod tests {
} }
} }
impl SimplePusPacketHandler for Pus17HandlerWithStoreTester { impl SimplePusPacketHandler for Pus17HandlerWithStoreTester {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { fn handle_one_tc(
&mut self,
) -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.poll_and_handle_next_tc(&time_stamp) self.handler
.poll_and_handle_next_tc(|_partial_error: &PartialPusHandlingError| {}, &time_stamp)
} }
} }
@ -276,9 +264,12 @@ mod tests {
} }
} }
impl SimplePusPacketHandler for Pus17HandlerWithVecTester { impl SimplePusPacketHandler for Pus17HandlerWithVecTester {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> { fn handle_one_tc(
&mut self,
) -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.poll_and_handle_next_tc(&time_stamp) self.handler
.poll_and_handle_next_tc(|_partial_error: &PartialPusHandlingError| {}, &time_stamp)
} }
} }
@ -328,10 +319,11 @@ mod tests {
let mut test_harness = Pus17HandlerWithStoreTester::new(0); let mut test_harness = Pus17HandlerWithStoreTester::new(0);
let result = test_harness.handle_one_tc(); let result = test_harness.handle_one_tc();
assert!(result.is_ok()); assert!(result.is_ok());
let result = result.unwrap(); match result.unwrap() {
if let PusPacketHandlerResult::Empty = result { DirectPusPacketHandlerResult::Handled(handled) => {
} else { assert_eq!(handled, HandlingStatus::Empty);
panic!("unexpected result type {result:?}") }
_ => panic!("unexpected result"),
} }
} }
@ -367,7 +359,7 @@ mod tests {
let result = test_harness.handle_one_tc(); let result = test_harness.handle_one_tc();
assert!(result.is_ok()); assert!(result.is_ok());
let result = result.unwrap(); let result = result.unwrap();
if let PusPacketHandlerResult::CustomSubservice(subservice, _) = result { if let DirectPusPacketHandlerResult::CustomSubservice(subservice, _) = result {
assert_eq!(subservice, 200); assert_eq!(subservice, 200);
} else { } else {
panic!("unexpected result type {result:?}") panic!("unexpected result type {result:?}")

View File

@ -79,6 +79,7 @@
//! The [integration test](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-core/tests/verification_test.rs) //! The [integration test](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-core/tests/verification_test.rs)
//! for the verification module contains examples how this module could be used in a more complex //! for the verification module contains examples how this module could be used in a more complex
//! context involving multiple threads //! context involving multiple threads
use crate::params::{Params, WritableToBeBytes};
use crate::pus::{source_buffer_large_enough, EcssTmSender, EcssTmtcError}; use crate::pus::{source_buffer_large_enough, EcssTmSender, EcssTmtcError};
use core::fmt::{Debug, Display, Formatter}; use core::fmt::{Debug, Display, Formatter};
use core::hash::{Hash, Hasher}; use core::hash::{Hash, Hasher};
@ -353,7 +354,7 @@ pub struct FailParams<'stamp, 'fargs> {
impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> { impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> {
pub fn new( pub fn new(
time_stamp: &'stamp [u8], time_stamp: &'stamp [u8],
failure_code: &'fargs impl EcssEnumeration, failure_code: &'fargs dyn EcssEnumeration,
failure_data: &'fargs [u8], failure_data: &'fargs [u8],
) -> Self { ) -> Self {
Self { Self {
@ -381,7 +382,7 @@ impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> {
pub fn new( pub fn new(
time_stamp: &'stamp [u8], time_stamp: &'stamp [u8],
step: &'fargs impl EcssEnumeration, step: &'fargs impl EcssEnumeration,
failure_code: &'fargs impl EcssEnumeration, failure_code: &'fargs dyn EcssEnumeration,
failure_data: &'fargs [u8], failure_data: &'fargs [u8],
) -> Self { ) -> Self {
Self { Self {
@ -1171,26 +1172,143 @@ pub mod alloc_mod {
} }
} }
/* pub struct FailParamHelper<'stamp, 'fargs, 'buf, 'params> {
#[cfg(feature = "std")] pub timestamp: &'stamp [u8],
pub mod std_mod { pub error_code: &'fargs dyn EcssEnumeration,
use std::sync::mpsc; pub small_data_buf: &'buf mut [u8],
pub params: Option<&'params Params>,
use crate::pool::StoreAddr; }
use crate::pus::verification::VerificationReporterWithSender;
/// This helper function simplifies generating completion failures where the error data has
use super::alloc_mod::VerificationReporterWithSharedPoolSender; /// the generic [Params] type.
///
pub type VerificationReporterWithSharedPoolMpscSender = /// A small data buffer needs to be supplied for the [Params::Heapless] type because all data
VerificationReporterWithSharedPoolSender<mpsc::Sender<StoreAddr>>; /// suplied as error data must be held in a slice. Passing a static buffer avoids dynamic memory
pub type VerificationReporterWithSharedPoolMpscBoundedSender = /// allocation for this case.
VerificationReporterWithSharedPoolSender<mpsc::SyncSender<StoreAddr>>; ///
pub type VerificationReporterWithVecMpscSender = /// Please note that this specific function can not propagate the [Params::Store] variant.
VerificationReporterWithSender<mpsc::Sender<alloc::vec::Vec<u8>>>; /// This function also might not be able to propagate other error variants which are added in
pub type VerificationReporterWithVecMpscBoundedSender = /// the future. The returned boolean on success denotes whether the error parameters were
VerificationReporterWithSender<mpsc::SyncSender<alloc::vec::Vec<u8>>>; /// propagated properly.
pub fn handle_completion_failure_with_generic_params<TcState: WasAtLeastAccepted + Copy>(
tm_sender: &(impl EcssTmSender + ?Sized),
verif_token: VerificationToken<TcState>,
verif_reporter: &impl VerificationReportingProvider,
helper: FailParamHelper,
) -> Result<bool, EcssTmtcError> {
let mut error_params_propagated = true;
if helper.params.is_none() {
verif_reporter.completion_failure(
tm_sender,
verif_token,
FailParams::new(helper.timestamp, helper.error_code, &[]),
)?;
return Ok(true);
}
let error_params = helper.params.unwrap();
match error_params {
Params::Heapless(heapless_param) => {
heapless_param
.write_to_be_bytes(&mut helper.small_data_buf[..heapless_param.written_len()])?;
verif_reporter.completion_failure(
tm_sender,
verif_token,
FailParams::new(
helper.timestamp,
helper.error_code,
&helper.small_data_buf[..heapless_param.written_len()],
),
)?;
}
#[cfg(feature = "alloc")]
Params::Vec(vec) => {
verif_reporter.completion_failure(
tm_sender,
verif_token,
FailParams::new(helper.timestamp, helper.error_code, vec),
)?;
}
#[cfg(feature = "alloc")]
Params::String(str) => {
verif_reporter.completion_failure(
tm_sender,
verif_token,
FailParams::new(helper.timestamp, helper.error_code, str.as_bytes()),
)?;
}
_ => {
verif_reporter.completion_failure(
tm_sender,
verif_token,
FailParams::new(helper.timestamp, helper.error_code, &[]),
)?;
error_params_propagated = false;
}
}
Ok(error_params_propagated)
}
/// This function is similar to [handle_completion_failure_with_error_as_params] but handles the
/// step failure case.
pub fn handle_step_failure_with_generic_params(
tm_sender: &(impl EcssTmSender + ?Sized),
verif_token: VerificationToken<TcStateStarted>,
verif_reporter: &impl VerificationReportingProvider,
helper: FailParamHelper,
step: &impl EcssEnumeration,
) -> Result<bool, EcssTmtcError> {
if helper.params.is_none() {
verif_reporter.step_failure(
tm_sender,
verif_token,
FailParamsWithStep::new(helper.timestamp, step, helper.error_code, &[]),
)?;
return Ok(true);
}
let error_params = helper.params.unwrap();
let mut error_params_propagated = true;
match error_params {
Params::Heapless(heapless_param) => {
heapless_param
.write_to_be_bytes(&mut helper.small_data_buf[..heapless_param.written_len()])?;
verif_reporter.step_failure(
tm_sender,
verif_token,
FailParamsWithStep::new(
helper.timestamp,
step,
helper.error_code,
&helper.small_data_buf[..heapless_param.written_len()],
),
)?;
}
#[cfg(feature = "alloc")]
Params::Vec(vec) => {
verif_reporter.step_failure(
tm_sender,
verif_token,
FailParamsWithStep::new(helper.timestamp, step, helper.error_code, vec),
)?;
}
#[cfg(feature = "alloc")]
Params::String(str) => {
verif_reporter.step_failure(
tm_sender,
verif_token,
FailParamsWithStep::new(helper.timestamp, step, helper.error_code, str.as_bytes()),
)?;
}
_ => {
verif_reporter.step_failure(
tm_sender,
verif_token,
FailParamsWithStep::new(helper.timestamp, step, helper.error_code, &[]),
)?;
error_params_propagated = false;
}
}
Ok(error_params_propagated)
} }
*/
#[cfg(any(feature = "test_util", test))] #[cfg(any(feature = "test_util", test))]
pub mod test_util { pub mod test_util {
@ -1566,73 +1684,19 @@ pub mod test_util {
.pop_front() .pop_front()
.expect("report queue is empty") .expect("report queue is empty")
} }
/*
pub fn verification_info(&self, req_id: &RequestId) -> Option<VerificationStatus> {
let verif_map = self.verification_map.lock().unwrap();
let value = verif_map.borrow().get(req_id).cloned();
value
}
pub fn check_started(&self, req_id: &RequestId) -> bool {
let verif_map = self.verification_map.lock().unwrap();
if let Some(entry) = verif_map.borrow().get(req_id) {
return entry.started.unwrap_or(false);
}
false
}
fn generic_completion_checks(
entry: &VerificationStatus,
step: Option<u16>,
completion_success: bool,
) {
assert!(entry.accepted.unwrap());
assert!(entry.started.unwrap());
if let Some(step) = step {
assert!(entry.step_status.unwrap());
assert_eq!(entry.step, step);
} else {
assert!(entry.step_status.is_none());
}
assert_eq!(entry.completed.unwrap(), completion_success);
}
pub fn assert_completion_failure(
&self,
req_id: &RequestId,
step: Option<u16>,
error_code: u64,
) {
let verif_map = self.verification_map.lock().unwrap();
if let Some(entry) = verif_map.borrow().get(req_id) {
Self::generic_completion_checks(entry, step, false);
assert_eq!(entry.fail_enum.unwrap(), error_code);
return;
}
panic!("request not in verification map");
}
pub fn completion_status(&self, req_id: &RequestId) -> Option<bool> {
let verif_map = self.verification_map.lock().unwrap();
if let Some(entry) = verif_map.borrow().get(req_id) {
return entry.completed;
}
panic!("request not in verification map");
}
*/
} }
} }
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use crate::params::Params;
use crate::pool::{SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig}; use crate::pool::{SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
use crate::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0}; use crate::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
use crate::pus::tests::CommonTmInfo; use crate::pus::tests::CommonTmInfo;
use crate::pus::verification::{ use crate::pus::verification::{
EcssTmSender, EcssTmtcError, FailParams, FailParamsWithStep, RequestId, TcStateNone, handle_step_failure_with_generic_params, EcssTmSender, EcssTmtcError, FailParams,
VerificationReporter, VerificationReporterCfg, VerificationToken, FailParamsWithStep, RequestId, TcStateNone, VerificationReporter, VerificationReporterCfg,
VerificationToken,
}; };
use crate::pus::{ChannelWithId, PusTmVariant}; use crate::pus::{ChannelWithId, PusTmVariant};
use crate::request::MessageMetadata; use crate::request::MessageMetadata;
@ -1640,6 +1704,7 @@ pub mod tests {
use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool}; use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool};
use crate::ComponentId; use crate::ComponentId;
use alloc::format; use alloc::format;
use alloc::string::ToString;
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader}; use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
use spacepackets::ecss::{ use spacepackets::ecss::{
EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusError, PusPacket, EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusError, PusPacket,
@ -1654,8 +1719,9 @@ pub mod tests {
use std::vec::Vec; use std::vec::Vec;
use super::{ use super::{
DummyVerificationHook, SeqCountProviderSimple, TcStateAccepted, TcStateStarted, handle_completion_failure_with_generic_params, DummyVerificationHook, FailParamHelper,
VerificationHookProvider, VerificationReportingProvider, WasAtLeastAccepted, SeqCountProviderSimple, TcStateAccepted, TcStateStarted, VerificationHookProvider,
VerificationReportingProvider, WasAtLeastAccepted,
}; };
fn is_send<T: Send>(_: &T) {} fn is_send<T: Send>(_: &T) {}
@ -1663,6 +1729,7 @@ pub mod tests {
fn is_sync<T: Sync>(_: &T) {} fn is_sync<T: Sync>(_: &T) {}
const EMPTY_STAMP: [u8; 7] = [0; 7]; const EMPTY_STAMP: [u8; 7] = [0; 7];
const DUMMY_STAMP: &[u8] = &[0, 1, 0, 1, 0, 1, 0];
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]
struct TmInfo { struct TmInfo {
@ -1740,8 +1807,8 @@ pub mod tests {
tc: Vec<u8>, tc: Vec<u8>,
} }
fn base_reporter(id: ComponentId) -> VerificationReporter { fn base_reporter(id: ComponentId, max_fail_data_len: usize) -> VerificationReporter {
let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, max_fail_data_len).unwrap();
VerificationReporter::new(id, &cfg) VerificationReporter::new(id, &cfg)
} }
@ -1844,66 +1911,57 @@ pub mod tests {
.completion_failure(&self.sender, token, params) .completion_failure(&self.sender, token, params)
} }
fn completion_success_check(&mut self, incrementing_couters: bool) { fn check_acceptance_success(&self, timestamp: &[u8; 7]) {
assert_eq!(self.sender.service_queue.borrow().len(), 3);
let mut current_seq_count = 0;
let cmp_info = TmInfo { let cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo { common: CommonTmInfo::new(1, TEST_APID, 0, 0, self.reporter.dest_id(), timestamp),
subservice: 1,
apid: TEST_APID,
seq_count: current_seq_count,
msg_counter: current_seq_count,
dest_id: self.reporter.dest_id(),
time_stamp: EMPTY_STAMP,
},
additional_data: None, additional_data: None,
}; };
let mut info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); let mut service_queue = self.sender.service_queue.borrow_mut();
assert!(service_queue.len() >= 1);
let info = service_queue.pop_front().unwrap();
assert_eq!(info, cmp_info); assert_eq!(info, cmp_info);
}
if incrementing_couters { fn check_start_success(&mut self, seq_count: u16, msg_counter: u16, timestamp: &[u8]) {
current_seq_count += 1; let mut srv_queue = self.sender.service_queue.borrow_mut();
}
let cmp_info = TmInfo { let cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo { common: CommonTmInfo::new(
subservice: 3, 3,
apid: TEST_APID, TEST_APID,
msg_counter: current_seq_count, seq_count,
seq_count: current_seq_count, msg_counter,
dest_id: self.reporter.dest_id(), self.reporter.dest_id(),
time_stamp: [0, 1, 0, 1, 0, 1, 0], timestamp,
}, ),
additional_data: None, additional_data: None,
}; };
info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); let info = srv_queue.pop_front().unwrap();
assert_eq!(info, cmp_info); assert_eq!(info, cmp_info);
}
if incrementing_couters { fn check_completion_success(&mut self, seq_count: u16, msg_counter: u16) {
current_seq_count += 1;
}
let cmp_info = TmInfo { let cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo { common: CommonTmInfo::new(
subservice: 7, 7,
apid: TEST_APID, TEST_APID,
msg_counter: current_seq_count, seq_count,
seq_count: current_seq_count, msg_counter,
dest_id: self.reporter.dest_id(), self.reporter.dest_id(),
time_stamp: EMPTY_STAMP, &EMPTY_STAMP,
}, ),
additional_data: None, additional_data: None,
}; };
info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); let info = self.sender.service_queue.borrow_mut().pop_front().unwrap();
assert_eq!(info, cmp_info); assert_eq!(info, cmp_info);
} }
} }
impl VerificationReporterTestbench<DummyVerificationHook> { impl VerificationReporterTestbench<DummyVerificationHook> {
fn new(id: ComponentId, tc: PusTcCreator) -> Self { fn new(id: ComponentId, tc: PusTcCreator, max_fail_data_len: usize) -> Self {
let reporter = base_reporter(id); let reporter = base_reporter(id, max_fail_data_len);
Self { Self {
id, id,
sender: TestSender::default(), sender: TestSender::default(),
@ -1913,36 +1971,10 @@ pub mod tests {
} }
} }
fn acceptance_check(&self, time_stamp: &[u8; 7]) { fn check_acceptance_failure(&mut self, timestamp: &[u8; 7]) {
let cmp_info = TmInfo { let cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo { common: CommonTmInfo::new(2, TEST_APID, 0, 0, self.reporter.dest_id(), timestamp),
subservice: 1,
apid: TEST_APID,
seq_count: 0,
msg_counter: 0,
dest_id: self.reporter.dest_id(),
time_stamp: *time_stamp,
},
additional_data: None,
};
let mut service_queue = self.sender.service_queue.borrow_mut();
assert_eq!(service_queue.len(), 1);
let info = service_queue.pop_front().unwrap();
assert_eq!(info, cmp_info);
}
fn acceptance_fail_check(&mut self, stamp_buf: [u8; 7]) {
let cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo {
subservice: 2,
seq_count: 0,
apid: TEST_APID,
msg_counter: 0,
dest_id: self.reporter.dest_id(),
time_stamp: stamp_buf,
},
additional_data: Some([0, 2].to_vec()), additional_data: Some([0, 2].to_vec()),
}; };
let service_queue = self.sender.service_queue.get_mut(); let service_queue = self.sender.service_queue.get_mut();
@ -1951,12 +1983,12 @@ pub mod tests {
assert_eq!(info, cmp_info); assert_eq!(info, cmp_info);
} }
fn start_fail_check(&mut self, fail_data_raw: [u8; 4]) { fn check_start_failure(&mut self, fail_data_raw: [u8; 4]) {
let mut srv_queue = self.sender.service_queue.borrow_mut(); let mut srv_queue = self.sender.service_queue.borrow_mut();
assert_eq!(srv_queue.len(), 2); assert_eq!(srv_queue.len(), 2);
let mut cmp_info = TmInfo { let mut cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(1, TEST_APID, 0, EMPTY_STAMP), common: CommonTmInfo::new_zero_seq_count(1, TEST_APID, 0, &EMPTY_STAMP),
additional_data: None, additional_data: None,
}; };
let mut info = srv_queue.pop_front().unwrap(); let mut info = srv_queue.pop_front().unwrap();
@ -1964,148 +1996,67 @@ pub mod tests {
cmp_info = TmInfo { cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(4, TEST_APID, 0, EMPTY_STAMP), common: CommonTmInfo::new_zero_seq_count(4, TEST_APID, 0, &EMPTY_STAMP),
additional_data: Some([&[22], fail_data_raw.as_slice()].concat().to_vec()), additional_data: Some([&[22], fail_data_raw.as_slice()].concat().to_vec()),
}; };
info = srv_queue.pop_front().unwrap(); info = srv_queue.pop_front().unwrap();
assert_eq!(info, cmp_info); assert_eq!(info, cmp_info);
} }
fn step_success_check(&mut self, time_stamp: &[u8; 7]) { fn check_step_success(&mut self, step: u8, timestamp: &[u8; 7]) {
let mut cmp_info = TmInfo { let cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(1, TEST_APID, 0, *time_stamp), common: CommonTmInfo::new_zero_seq_count(5, TEST_APID, 0, timestamp),
additional_data: None, additional_data: Some([step].to_vec()),
}; };
let mut srv_queue = self.sender.service_queue.borrow_mut(); let mut srv_queue = self.sender.service_queue.borrow_mut();
let mut info = srv_queue.pop_front().unwrap(); let info = srv_queue.pop_front().unwrap();
assert_eq!(info, cmp_info);
cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(3, TEST_APID, 0, *time_stamp),
additional_data: None,
};
info = srv_queue.pop_front().unwrap();
assert_eq!(info, cmp_info);
cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(5, TEST_APID, 0, *time_stamp),
additional_data: Some([0].to_vec()),
};
info = srv_queue.pop_front().unwrap();
assert_eq!(info, cmp_info);
cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(5, TEST_APID, 0, *time_stamp),
additional_data: Some([1].to_vec()),
};
info = srv_queue.pop_front().unwrap();
assert_eq!(info, cmp_info); assert_eq!(info, cmp_info);
} }
fn check_step_failure(&mut self, fail_data_raw: [u8; 4]) { fn check_step_failure(
assert_eq!(self.sender.service_queue.borrow().len(), 4); &mut self,
let mut cmp_info = TmInfo { step: &impl EcssEnumeration,
requestor: MessageMetadata::new(self.request_id.into(), self.id), error_code: &impl EcssEnumeration,
common: CommonTmInfo::new_zero_seq_count( fail_data: &[u8],
1, ) {
TEST_APID, let mut additional_data = Vec::new();
self.reporter.dest_id(), additional_data.extend(step.to_vec());
EMPTY_STAMP, additional_data.extend(error_code.to_vec());
), additional_data.extend(fail_data);
additional_data: None, let cmp_info = TmInfo {
};
let mut info = self.sender.service_queue.borrow_mut().pop_front().unwrap();
assert_eq!(info, cmp_info);
cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(
3,
TEST_APID,
self.reporter.dest_id(),
[0, 1, 0, 1, 0, 1, 0],
),
additional_data: None,
};
info = self.sender.service_queue.borrow_mut().pop_front().unwrap();
assert_eq!(info, cmp_info);
cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(
5,
TEST_APID,
self.reporter.dest_id(),
EMPTY_STAMP,
),
additional_data: Some([0].to_vec()),
};
info = self.sender.service_queue.get_mut().pop_front().unwrap();
assert_eq!(info, cmp_info);
cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count( common: CommonTmInfo::new_zero_seq_count(
6, 6,
TEST_APID, TEST_APID,
self.reporter.dest_id(), self.reporter.dest_id(),
EMPTY_STAMP, &EMPTY_STAMP,
),
additional_data: Some(
[
[1].as_slice(),
&[0, 0, 0x10, 0x20],
fail_data_raw.as_slice(),
]
.concat()
.to_vec(),
), ),
additional_data: Some(additional_data),
}; };
info = self.sender.service_queue.get_mut().pop_front().unwrap(); let info = self.sender.service_queue.get_mut().pop_front().unwrap();
assert_eq!(info, cmp_info); assert_eq!(info, cmp_info);
} }
fn completion_fail_check(&mut self) { fn check_completion_failure(
assert_eq!(self.sender.service_queue.borrow().len(), 3); &mut self,
error_code: &impl EcssEnumeration,
let mut cmp_info = TmInfo { fail_data: &[u8],
requestor: MessageMetadata::new(self.request_id.into(), self.id), ) {
common: CommonTmInfo::new_zero_seq_count( let mut additional_data = Vec::new();
1, additional_data.extend(error_code.to_vec());
TEST_APID, additional_data.extend(fail_data);
self.reporter.dest_id(), let cmp_info = TmInfo {
EMPTY_STAMP,
),
additional_data: None,
};
let mut info = self.sender.service_queue.get_mut().pop_front().unwrap();
assert_eq!(info, cmp_info);
cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count(
3,
TEST_APID,
self.reporter.dest_id(),
[0, 1, 0, 1, 0, 1, 0],
),
additional_data: None,
};
info = self.sender.service_queue.get_mut().pop_front().unwrap();
assert_eq!(info, cmp_info);
cmp_info = TmInfo {
requestor: MessageMetadata::new(self.request_id.into(), self.id), requestor: MessageMetadata::new(self.request_id.into(), self.id),
common: CommonTmInfo::new_zero_seq_count( common: CommonTmInfo::new_zero_seq_count(
8, 8,
TEST_APID, TEST_APID,
self.reporter.dest_id(), self.reporter.dest_id(),
EMPTY_STAMP, &EMPTY_STAMP,
), ),
additional_data: Some([0, 0, 0x10, 0x20].to_vec()), additional_data: Some(additional_data),
}; };
info = self.sender.service_queue.get_mut().pop_front().unwrap(); let info = self.sender.service_queue.get_mut().pop_front().unwrap();
assert_eq!(info, cmp_info); assert_eq!(info, cmp_info);
} }
} }
@ -2128,7 +2079,7 @@ pub mod tests {
#[test] #[test]
fn test_state() { fn test_state() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
assert_eq!(testbench.reporter.apid(), TEST_APID); assert_eq!(testbench.reporter.apid(), TEST_APID);
testbench.reporter.set_apid(TEST_APID + 1); testbench.reporter.set_apid(TEST_APID + 1);
assert_eq!(testbench.reporter.apid(), TEST_APID + 1); assert_eq!(testbench.reporter.apid(), TEST_APID + 1);
@ -2136,43 +2087,43 @@ pub mod tests {
#[test] #[test]
fn test_basic_acceptance_success() { fn test_basic_acceptance_success() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let token = testbench.init(); let token = testbench.init();
testbench testbench
.acceptance_success(token, &EMPTY_STAMP) .acceptance_success(token, &EMPTY_STAMP)
.expect("sending acceptance success failed"); .expect("sending acceptance success failed");
testbench.acceptance_check(&EMPTY_STAMP); testbench.check_acceptance_success(&EMPTY_STAMP);
} }
#[test] #[test]
fn test_basic_acceptance_failure() { fn test_basic_acceptance_failure() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let init_token = testbench.init(); let init_token = testbench.init();
let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; let timestamp = [1, 2, 3, 4, 5, 6, 7];
let fail_code = EcssEnumU16::new(2); let fail_code = EcssEnumU16::new(2);
let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code); let fail_params = FailParams::new_no_fail_data(timestamp.as_slice(), &fail_code);
testbench testbench
.acceptance_failure(init_token, fail_params) .acceptance_failure(init_token, fail_params)
.expect("sending acceptance failure failed"); .expect("sending acceptance failure failed");
testbench.acceptance_fail_check(stamp_buf); testbench.check_acceptance_failure(&timestamp);
} }
#[test] #[test]
fn test_basic_acceptance_failure_with_helper() { fn test_basic_acceptance_failure_with_helper() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let init_token = testbench.init(); let init_token = testbench.init();
let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; let timestamp = [1, 2, 3, 4, 5, 6, 7];
let fail_code = EcssEnumU16::new(2); let fail_code = EcssEnumU16::new(2);
let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code); let fail_params = FailParams::new_no_fail_data(timestamp.as_slice(), &fail_code);
testbench testbench
.acceptance_failure(init_token, fail_params) .acceptance_failure(init_token, fail_params)
.expect("sending acceptance failure failed"); .expect("sending acceptance failure failed");
testbench.acceptance_fail_check(stamp_buf); testbench.check_acceptance_failure(&timestamp);
} }
#[test] #[test]
fn test_acceptance_fail_data_too_large() { fn test_acceptance_fail_data_too_large() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 8);
let init_token = testbench.init(); let init_token = testbench.init();
let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; let stamp_buf = [1, 2, 3, 4, 5, 6, 7];
let fail_code = EcssEnumU16::new(2); let fail_code = EcssEnumU16::new(2);
@ -2204,7 +2155,7 @@ pub mod tests {
#[test] #[test]
fn test_basic_acceptance_failure_with_fail_data() { fn test_basic_acceptance_failure_with_fail_data() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let fail_code = EcssEnumU8::new(10); let fail_code = EcssEnumU8::new(10);
let fail_data = EcssEnumU32::new(12); let fail_data = EcssEnumU32::new(12);
let mut fail_data_raw = [0; 4]; let mut fail_data_raw = [0; 4];
@ -2216,7 +2167,7 @@ pub mod tests {
.expect("sending acceptance failure failed"); .expect("sending acceptance failure failed");
let cmp_info = TmInfo { let cmp_info = TmInfo {
requestor: MessageMetadata::new(testbench.request_id.into(), testbench.id), requestor: MessageMetadata::new(testbench.request_id.into(), testbench.id),
common: CommonTmInfo::new_zero_seq_count(2, TEST_APID, 0, EMPTY_STAMP), common: CommonTmInfo::new_zero_seq_count(2, TEST_APID, 0, &EMPTY_STAMP),
additional_data: Some([10, 0, 0, 0, 12].to_vec()), additional_data: Some([10, 0, 0, 0, 12].to_vec()),
}; };
let mut service_queue = testbench.sender.service_queue.borrow_mut(); let mut service_queue = testbench.sender.service_queue.borrow_mut();
@ -2227,7 +2178,7 @@ pub mod tests {
#[test] #[test]
fn test_start_failure() { fn test_start_failure() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let init_token = testbench.init(); let init_token = testbench.init();
let fail_code = EcssEnumU8::new(22); let fail_code = EcssEnumU8::new(22);
let fail_data: i32 = -12; let fail_data: i32 = -12;
@ -2241,12 +2192,12 @@ pub mod tests {
testbench testbench
.start_failure(accepted_token, fail_params) .start_failure(accepted_token, fail_params)
.expect("Start failure failure"); .expect("Start failure failure");
testbench.start_fail_check(fail_data_raw); testbench.check_start_failure(fail_data_raw);
} }
#[test] #[test]
fn test_start_failure_with_helper() { fn test_start_failure_with_helper() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let token = testbench.init(); let token = testbench.init();
let fail_code = EcssEnumU8::new(22); let fail_code = EcssEnumU8::new(22);
let fail_data: i32 = -12; let fail_data: i32 = -12;
@ -2260,12 +2211,12 @@ pub mod tests {
testbench testbench
.start_failure(accepted_token, fail_params) .start_failure(accepted_token, fail_params)
.expect("start failure failed"); .expect("start failure failed");
testbench.start_fail_check(fail_data_raw); testbench.check_start_failure(fail_data_raw);
} }
#[test] #[test]
fn test_steps_success() { fn test_steps_success() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let token = testbench.init(); let token = testbench.init();
let accepted_token = testbench let accepted_token = testbench
.acceptance_success(token, &EMPTY_STAMP) .acceptance_success(token, &EMPTY_STAMP)
@ -2280,12 +2231,15 @@ pub mod tests {
.step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(1)) .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(1))
.expect("step 1 failed"); .expect("step 1 failed");
assert_eq!(testbench.sender.service_queue.borrow().len(), 4); assert_eq!(testbench.sender.service_queue.borrow().len(), 4);
testbench.step_success_check(&EMPTY_STAMP); testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(0, 0, &EMPTY_STAMP);
testbench.check_step_success(0, &EMPTY_STAMP);
testbench.check_step_success(1, &EMPTY_STAMP);
} }
#[test] #[test]
fn test_step_failure() { fn test_step_failure() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let token = testbench.init(); let token = testbench.init();
let fail_code = EcssEnumU32::new(0x1020); let fail_code = EcssEnumU32::new(0x1020);
let fail_data: f32 = -22.3232; let fail_data: f32 = -22.3232;
@ -2303,7 +2257,7 @@ pub mod tests {
.acceptance_success(token, &EMPTY_STAMP) .acceptance_success(token, &EMPTY_STAMP)
.expect("Sending acceptance success failed"); .expect("Sending acceptance success failed");
let started_token = testbench let started_token = testbench
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) .start_success(accepted_token, DUMMY_STAMP)
.expect("Sending start success failed"); .expect("Sending start success failed");
testbench testbench
.step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0)) .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0))
@ -2311,12 +2265,15 @@ pub mod tests {
testbench testbench
.step_failure(started_token, fail_params) .step_failure(started_token, fail_params)
.expect("Step failure failed"); .expect("Step failure failed");
testbench.check_step_failure(fail_data_raw); testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(0, 0, DUMMY_STAMP);
testbench.check_step_success(0, &EMPTY_STAMP);
testbench.check_step_failure(&fail_step, &fail_code, &fail_data_raw);
} }
#[test] #[test]
fn test_completion_failure() { fn test_completion_failure() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16);
let token = testbench.init(); let token = testbench.init();
let fail_code = EcssEnumU32::new(0x1020); let fail_code = EcssEnumU32::new(0x1020);
let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code); let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code);
@ -2325,29 +2282,34 @@ pub mod tests {
.acceptance_success(token, &EMPTY_STAMP) .acceptance_success(token, &EMPTY_STAMP)
.expect("Sending acceptance success failed"); .expect("Sending acceptance success failed");
let started_token = testbench let started_token = testbench
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) .start_success(accepted_token, DUMMY_STAMP)
.expect("Sending start success failed"); .expect("Sending start success failed");
testbench testbench
.completion_failure(started_token, fail_params) .completion_failure(started_token, fail_params)
.expect("Completion failure"); .expect("Completion failure");
testbench.completion_fail_check(); testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(0, 0, DUMMY_STAMP);
testbench.check_completion_failure(&fail_code, &[]);
} }
#[test] #[test]
fn test_complete_success_sequence() { fn test_complete_success_sequence() {
let mut testbench = let mut testbench =
VerificationReporterTestbench::new(TEST_COMPONENT_ID_0.id(), create_generic_ping()); VerificationReporterTestbench::new(TEST_COMPONENT_ID_0.id(), create_generic_ping(), 16);
let token = testbench.init(); let token = testbench.init();
let accepted_token = testbench let accepted_token = testbench
.acceptance_success(token, &EMPTY_STAMP) .acceptance_success(token, &EMPTY_STAMP)
.expect("Sending acceptance success failed"); .expect("Sending acceptance success failed");
let started_token = testbench let started_token = testbench
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) .start_success(accepted_token, DUMMY_STAMP)
.expect("Sending start success failed"); .expect("Sending start success failed");
testbench testbench
.completion_success(started_token, &EMPTY_STAMP) .completion_success(started_token, &EMPTY_STAMP)
.expect("Sending completion success failed"); .expect("Sending completion success failed");
testbench.completion_success_check(false); testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(0, 0, DUMMY_STAMP);
testbench.check_completion_success(0, 0);
} }
#[test] #[test]
@ -2367,6 +2329,83 @@ pub mod tests {
testbench testbench
.completion_success(started_token, &EMPTY_STAMP) .completion_success(started_token, &EMPTY_STAMP)
.expect("Sending completion success failed"); .expect("Sending completion success failed");
testbench.completion_success_check(true); testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(1, 1, DUMMY_STAMP);
testbench.check_completion_success(2, 2);
}
#[test]
fn test_completion_failure_helper_string_param() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 32);
let token = testbench.init();
let accepted_token = testbench
.acceptance_success(token, &EMPTY_STAMP)
.expect("Sending acceptance success failed");
let mut small_data_buf: [u8; 16] = [0; 16];
let fail_code = EcssEnumU8::new(1);
let fail_data = "error 404 oh no".to_string();
let fail_params = Params::String(fail_data.clone());
let result = handle_completion_failure_with_generic_params(
&testbench.sender,
accepted_token,
&testbench.reporter,
FailParamHelper {
timestamp: &EMPTY_STAMP,
error_code: &fail_code,
small_data_buf: &mut small_data_buf,
params: Some(&fail_params),
},
);
assert!(result.unwrap());
testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_completion_failure(&fail_code, fail_data.as_bytes());
}
#[test]
fn test_step_failure_helper_string_param() {
let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 32);
let token = testbench.init();
let accepted_token = testbench
.acceptance_success(token, &EMPTY_STAMP)
.expect("Sending acceptance success failed");
let started_token = testbench
.start_success(accepted_token, &EMPTY_STAMP)
.expect("Sending start success failed");
let mut small_data_buf: [u8; 16] = [0; 16];
let step = EcssEnumU8::new(2);
let fail_code = EcssEnumU8::new(1);
let fail_data = "AAAAAAAAAAAHHHHHH".to_string();
let fail_params = Params::String(fail_data.clone());
let result = handle_step_failure_with_generic_params(
&testbench.sender,
started_token,
&testbench.reporter,
FailParamHelper {
timestamp: &EMPTY_STAMP,
error_code: &fail_code,
small_data_buf: &mut small_data_buf,
params: Some(&fail_params),
},
&step,
);
assert!(result.unwrap());
testbench.check_acceptance_success(&EMPTY_STAMP);
testbench.check_start_success(0, 0, &EMPTY_STAMP);
testbench.check_step_failure(&step, &fail_code, fail_data.as_bytes());
}
#[test]
fn test_completion_failure_helper_vec_param() {
// TODO: Test this.
}
#[test]
fn test_completion_failure_helper_raw_param() {
// TODO: Test this.
}
#[test]
fn test_completion_failure_helper_store_param_ignored() {
// TODO: Test this.
} }
} }

View File

@ -10,7 +10,7 @@ pub use std_mod::*;
use spacepackets::{ use spacepackets::{
ecss::{tc::IsPusTelecommand, PusPacket}, ecss::{tc::IsPusTelecommand, PusPacket},
ByteConversionError, CcsdsPacket, ByteConversionError,
}; };
use crate::{queue::GenericTargetedMessagingError, ComponentId}; use crate::{queue::GenericTargetedMessagingError, ComponentId};
@ -47,7 +47,7 @@ impl UniqueApidTargetId {
/// This function attempts to build the ID from a PUS telecommand by extracting the APID /// This function attempts to build the ID from a PUS telecommand by extracting the APID
/// and the first four bytes of the application data field as the target field. /// and the first four bytes of the application data field as the target field.
pub fn from_pus_tc( pub fn from_pus_tc(
tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand), tc: &(impl PusPacket + IsPusTelecommand),
) -> Result<Self, ByteConversionError> { ) -> Result<Self, ByteConversionError> {
if tc.user_data().len() < 4 { if tc.user_data().len() < 4 {
return Err(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {

View File

@ -21,7 +21,7 @@ struct EventIntrospection {
} }
//#[event(descr="This is some info event")] //#[event(descr="This is some info event")]
const INFO_EVENT_0: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::const_new(0, 0); const INFO_EVENT_0: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(0, 0);
const INFO_EVENT_0_ERASED: EventU32 = EventU32::const_from_info(INFO_EVENT_0); const INFO_EVENT_0_ERASED: EventU32 = EventU32::const_from_info(INFO_EVENT_0);
// This is ideally auto-generated // This is ideally auto-generated
@ -36,7 +36,7 @@ const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection {
}; };
//#[event(descr="This is some low severity event")] //#[event(descr="This is some low severity event")]
const SOME_LOW_SEV_EVENT: EventU32TypedSev<SeverityLow> = EventU32TypedSev::const_new(0, 12); const SOME_LOW_SEV_EVENT: EventU32TypedSev<SeverityLow> = EventU32TypedSev::new(0, 12);
//const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT]; //const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT];
@ -47,7 +47,7 @@ const TEST_GROUP_NAME_NAME: &str = "TEST_GROUP_NAME";
//#[event(desc="Some medium severity event")] //#[event(desc="Some medium severity event")]
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: EventU32TypedSev<SeverityMedium> = const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: EventU32TypedSev<SeverityMedium> =
EventU32TypedSev::const_new(TEST_GROUP_NAME, 0); EventU32TypedSev::new(TEST_GROUP_NAME, 0);
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED: EventU32 = const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED: EventU32 =
EventU32::const_from_medium(MEDIUM_SEV_EVENT_IN_OTHER_GROUP); EventU32::const_from_medium(MEDIUM_SEV_EVENT_IN_OTHER_GROUP);
@ -62,7 +62,7 @@ const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION: EventIntrospection = EventI
info: "Some medium severity event", info: "Some medium severity event",
}; };
const CONST_SLICE: &'static [u8] = &[0, 1, 2, 3]; const CONST_SLICE: &[u8] = &[0, 1, 2, 3];
const INTROSPECTION_FOR_TEST_GROUP_0: [&EventIntrospection; 2] = const INTROSPECTION_FOR_TEST_GROUP_0: [&EventIntrospection; 2] =
[&INFO_EVENT_0_INTROSPECTION, &INFO_EVENT_0_INTROSPECTION]; [&INFO_EVENT_0_INTROSPECTION, &INFO_EVENT_0_INTROSPECTION];

View File

@ -1,11 +1,11 @@
use satrs::event_man::{ use satrs::event_man::{
EventManagerWithMpsc, EventMessage, EventMessageU32, EventRoutingError, EventSendProvider, EventManagerWithMpsc, EventMessage, EventMessageU32, EventRoutingError, EventSendProvider,
EventU32SenderMpsc, MpscEventU32Receiver, EventU32SenderMpsc,
}; };
use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
use satrs::params::U32Pair; use satrs::params::U32Pair;
use satrs::params::{Params, ParamsHeapless, WritableToBeBytes}; use satrs::params::{Params, ParamsHeapless, WritableToBeBytes};
use satrs::pus::event_man::{DefaultPusEventMgmtBackend, EventReporter, PusEventDispatcher}; use satrs::pus::event_man::{DefaultPusEventReportingMap, EventReporter, PusEventTmCreatorWithMap};
use satrs::pus::test_util::TEST_COMPONENT_ID_0; use satrs::pus::test_util::TEST_COMPONENT_ID_0;
use satrs::request::UniqueApidTargetId; use satrs::request::UniqueApidTargetId;
use satrs::tmtc::PacketAsVec; use satrs::tmtc::PacketAsVec;
@ -14,9 +14,8 @@ use spacepackets::ecss::{PusError, PusPacket};
use std::sync::mpsc::{self, SendError, TryRecvError}; use std::sync::mpsc::{self, SendError, TryRecvError};
use std::thread; use std::thread;
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::<SeverityInfo>::new(1, 0);
EventU32TypedSev::<SeverityInfo>::const_new(1, 0); const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5);
const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5);
const EMPTY_STAMP: [u8; 7] = [0; 7]; const EMPTY_STAMP: [u8; 7] = [0; 7];
const TEST_APID: u16 = 0x02; const TEST_APID: u16 = 0x02;
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
@ -29,18 +28,18 @@ pub enum CustomTmSenderError {
#[test] #[test]
fn test_threaded_usage() { fn test_threaded_usage() {
let (event_sender, event_man_receiver) = mpsc::channel(); let (event_tx, event_rx) = mpsc::sync_channel(100);
let event_receiver = MpscEventU32Receiver::new(event_man_receiver); let mut event_man = EventManagerWithMpsc::new(event_rx);
let mut event_man = EventManagerWithMpsc::new(event_receiver);
let (pus_event_man_tx, pus_event_man_rx) = mpsc::channel(); let (pus_event_man_tx, pus_event_man_rx) = mpsc::channel();
let pus_event_man_send_provider = EventU32SenderMpsc::new(1, pus_event_man_tx); let pus_event_man_send_provider = EventU32SenderMpsc::new(1, pus_event_man_tx);
event_man.subscribe_all(pus_event_man_send_provider.target_id()); event_man.subscribe_all(pus_event_man_send_provider.target_id());
event_man.add_sender(pus_event_man_send_provider); event_man.add_sender(pus_event_man_send_provider);
let (event_tx, event_rx) = mpsc::channel::<PacketAsVec>(); let (event_packet_tx, event_packet_rx) = mpsc::channel::<PacketAsVec>();
let reporter = let reporter =
EventReporter::new(TEST_ID.raw(), 0x02, 0, 128).expect("Creating event reporter failed"); EventReporter::new(TEST_ID.raw(), 0x02, 0, 128).expect("Creating event reporter failed");
let pus_event_man = PusEventDispatcher::new(reporter, DefaultPusEventMgmtBackend::default()); let pus_event_man =
PusEventTmCreatorWithMap::new(reporter, DefaultPusEventReportingMap::default());
let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| { let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| {
panic!("received routing error for event {event_msg:?}: {error:?}"); panic!("received routing error for event {event_msg:?}: {error:?}");
}; };
@ -54,7 +53,7 @@ fn test_threaded_usage() {
Ok(event_msg) => { Ok(event_msg) => {
let gen_event = |aux_data| { let gen_event = |aux_data| {
pus_event_man.generate_pus_event_tm_generic( pus_event_man.generate_pus_event_tm_generic(
&event_tx, &event_packet_tx,
&EMPTY_STAMP, &EMPTY_STAMP,
event_msg.event(), event_msg.event(),
aux_data, aux_data,
@ -100,14 +99,14 @@ fn test_threaded_usage() {
// Event sender and TM checker thread // Event sender and TM checker thread
let jh1 = thread::spawn(move || { let jh1 = thread::spawn(move || {
event_sender event_tx
.send(EventMessage::new( .send(EventMessage::new(
TEST_COMPONENT_ID_0.id(), TEST_COMPONENT_ID_0.id(),
INFO_EVENT.into(), INFO_EVENT.into(),
)) ))
.expect("Sending info event failed"); .expect("Sending info event failed");
loop { loop {
match event_rx.try_recv() { match event_packet_rx.try_recv() {
// Event TM received successfully // Event TM received successfully
Ok(event_tm) => { Ok(event_tm) => {
let tm = PusTmReader::new(event_tm.packet.as_slice(), 7) let tm = PusTmReader::new(event_tm.packet.as_slice(), 7)
@ -129,7 +128,7 @@ fn test_threaded_usage() {
} }
} }
} }
event_sender event_tx
.send(EventMessage::new_with_params( .send(EventMessage::new_with_params(
TEST_COMPONENT_ID_0.id(), TEST_COMPONENT_ID_0.id(),
LOW_SEV_EVENT, LOW_SEV_EVENT,
@ -137,7 +136,7 @@ fn test_threaded_usage() {
)) ))
.expect("Sending low severity event failed"); .expect("Sending low severity event failed");
loop { loop {
match event_rx.try_recv() { match event_packet_rx.try_recv() {
// Event TM received successfully // Event TM received successfully
Ok(event_tm) => { Ok(event_tm) => {
let tm = PusTmReader::new(event_tm.packet.as_slice(), 7) let tm = PusTmReader::new(event_tm.packet.as_slice(), 7)