Merge remote-tracking branch 'origin/main' into serialization-prototyping
All checks were successful
Rust/sat-rs/pipeline/head This commit looks good

This commit is contained in:
Robin Müller 2024-05-25 12:31:51 +02:00
commit a710b30013
48 changed files with 3018 additions and 1694 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 --all-features
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

1
.gitignore vendored
View File

@ -2,6 +2,7 @@ target/
output.log output.log
/Cargo.lock /Cargo.lock
output.log
output.log output.log

View File

@ -54,6 +54,17 @@ Each project has its own `CHANGELOG.md`.
[`satrs`](https://egit.irs.uni-stuttgart.de/rust/satrs/src/branch/main/satrs) [`satrs`](https://egit.irs.uni-stuttgart.de/rust/satrs/src/branch/main/satrs)
crate. crate.
# Flight Heritage
There is an active and continuous effort to get early flight heritage for the sat-rs library.
Currently this library has the following flight heritage:
- Submission as an [OPS-SAT experiment](https://www.esa.int/Enabling_Support/Operations/OPS-SAT)
which has also
[flown on the satellite](https://blogs.esa.int/rocketscience/2024/05/21/ops-sat-reentry-tomorrow-final-experiments-continue/).
The application is strongly based on the sat-rs example application. You can find the repository
of the experiment [here](https://egit.irs.uni-stuttgart.de/rust/ops-sat-rs).
# Coverage # Coverage
Coverage was generated using [`grcov`](https://github.com/mozilla/grcov). If you have not done so Coverage was generated using [`grcov`](https://github.com/mozilla/grcov). If you have not done so
@ -64,5 +75,5 @@ rustup component add llvm-tools-preview
cargo install grcov --locked cargo install grcov --locked
``` ```
After that, you can simply run `coverage.py` to test the `satrs-core` crate with coverage. You can After that, you can simply run `coverage.py` to test the `satrs` crate with coverage. You can
optionally supply the `--open` flag to open the coverage report in your webbrowser. optionally supply the `--open` flag to open the coverage report in your webbrowser.

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 --all-features'
} }
} }
stage('Check with all features') { stage('Check with all features') {

View File

@ -166,7 +166,7 @@ Subsystem<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:
<y:Geometry height="30.0" width="125.0" x="1151.9280499999995" y="281.84403125000006"/> <y:Geometry height="30.0" width="125.0" x="1151.9280499999995" y="281.84403125000006"/>
<y:Fill color="#CCFFFF" transparent="false"/> <y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/> <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="76.255859375" x="24.3720703125" xml:space="preserve" y="4.8515625">TM Funnel<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel> <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="58.837890625" x="33.0810546875" xml:space="preserve" y="4.8515625">TM Sink<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/> <y:Shape type="rectangle"/>
</y:ShapeNode> </y:ShapeNode>
</data> </data>
@ -260,7 +260,7 @@ Mode Tree<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:
<y:Geometry height="57.265600000000006" width="631.1152" x="810.8847999999999" y="411.39428125"/> <y:Geometry height="57.265600000000006" width="631.1152" x="810.8847999999999" y="411.39428125"/>
<y:Fill hasColor="false" transparent="false"/> <y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/> <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="261.8125" x="166.89412267941418" xml:space="preserve" y="3.144146301369915">satrs-satellite <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="261.8125" x="166.89412267941418" xml:space="preserve" y="3.144146301369915">satrs-minisim
Simulator based on asynchronix<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-0.028136269449041573" nodeRatioY="-0.08493150684931505" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel> Simulator based on asynchronix<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-0.028136269449041573" nodeRatioY="-0.08493150684931505" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/> <y:Shape type="rectangle"/>
</y:ShapeNode> </y:ShapeNode>
@ -272,7 +272,7 @@ Simulator based on asynchronix<y:LabelModel><y:SmartNodeLabelModel distance="4.0
<y:Geometry height="50.0" width="631.1152000000002" x="810.8847999999998" y="476.2958625000002"/> <y:Geometry height="50.0" width="631.1152000000002" x="810.8847999999998" y="476.2958625000002"/>
<y:Fill hasColor="false" transparent="false"/> <y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/> <y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="374.8359375" x="110.3824039294143" xml:space="preserve" y="0.12842465753431043">satrs-tmtc <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="374.8359375" x="110.3824039294143" xml:space="preserve" y="0.12842465753431043">pytmtc
Command-line interface based TMTC handling<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-0.028136269449041573" nodeRatioY="-0.08493150684931505" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel> Command-line interface based TMTC handling<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-0.028136269449041573" nodeRatioY="-0.08493150684931505" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/> <y:Shape type="rectangle"/>
</y:ShapeNode> </y:ShapeNode>

File diff suppressed because it is too large Load Diff

View File

@ -17,3 +17,8 @@ The following images shows how the flow of events could look like in a system wh
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:
![Event flow](images/events/event_man_arch.png) ![Event flow](images/events/event_man_arch.png)
For the concrete implementation of your own event management and/or event routing system, you
can have a look at the event management documentation inside the
[API documentation](https://docs.rs/satrs/latest/satrs/event_man/index.html) where you can also
find references to all examples.

View File

@ -32,3 +32,14 @@ The [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/m
provides various practical usage examples of the `sat-rs` framework. If you are more interested in provides various practical usage examples of the `sat-rs` framework. If you are more interested in
the practical application of `sat-rs` inside an application, it is recommended to have a look at the practical application of `sat-rs` inside an application, it is recommended to have a look at
the example application. the example application.
# Flight Heritage
There is an active and continuous effort to get early flight heritage for the sat-rs library.
Currently this library has the following flight heritage:
- Submission as an [OPS-SAT experiment](https://www.esa.int/Enabling_Support/Operations/OPS-SAT)
which has also
[flown on the satellite](https://blogs.esa.int/rocketscience/2024/05/21/ops-sat-reentry-tomorrow-final-experiments-continue/).
The application is strongly based on the sat-rs example application. You can find the repository
of the experiment [here](https://egit.irs.uni-stuttgart.de/rust/ops-sat-rs).

View File

@ -182,7 +182,7 @@ pub mod pool {
use super::*; use super::*;
pub fn create_static_pools() -> (StaticMemoryPool, StaticMemoryPool) { pub fn create_static_pools() -> (StaticMemoryPool, StaticMemoryPool) {
( (
StaticMemoryPool::new(StaticPoolConfig::new( StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![ vec![
(30, 32), (30, 32),
(15, 64), (15, 64),
@ -193,7 +193,7 @@ pub mod pool {
], ],
true, true,
)), )),
StaticMemoryPool::new(StaticPoolConfig::new( StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![ vec![
(30, 32), (30, 32),
(15, 64), (15, 64),
@ -208,7 +208,7 @@ pub mod pool {
} }
pub fn create_sched_tc_pool() -> StaticMemoryPool { pub fn create_sched_tc_pool() -> StaticMemoryPool {
StaticMemoryPool::new(StaticPoolConfig::new( StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![ vec![
(30, 32), (30, 32),
(15, 64), (15, 64),

View File

@ -2,7 +2,6 @@ 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;
@ -42,6 +41,7 @@ pub struct PusEventHandler<TmSender: EcssTmSender> {
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,
} }
@ -82,6 +82,7 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
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,
} }
@ -132,21 +133,26 @@ impl<TmSender: EcssTmSender> PusEventHandler<TmSender> {
// Perform the generation of PUS event packets // Perform the generation of PUS event packets
match self.pus_event_man_rx.try_recv() { match self.pus_event_man_rx.try_recv() {
Ok(event_msg) => { Ok(event_msg) => {
update_time(&mut self.time_provider, &mut self.timestamp);
let param_vec = event_msg.params().map_or(Vec::new(), |param| {
param.to_vec().expect("failed to convert params to vec")
});
// We use the TM modification hook to set the sender APID for each event. // 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.pus_event_tm_creator.reporter.tm_hook.next_apid =
UniqueApidTargetId::from(event_msg.sender_id()).apid; UniqueApidTargetId::from(event_msg.sender_id()).apid;
self.pus_event_tm_creator update_time(&mut self.time_provider, &mut self.timestamp);
.generate_pus_event_tm_generic( let generation_result = self
.pus_event_tm_creator
.generate_pus_event_tm_generic_with_generic_params(
&self.tm_sender, &self.tm_sender,
&self.timestamp, &self.timestamp,
event_msg.event(), event_msg.event(),
Some(&param_vec), &mut self.small_data_buf,
event_msg.params(),
) )
.expect("Sending TM as event failed"); .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 { Err(e) => match e {
mpsc::TryRecvError::Empty => break, mpsc::TryRecvError::Empty => break,

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

@ -458,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,24 +1,22 @@
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,
@ -35,7 +33,7 @@ pub fn create_test_service_static(
)); ));
TestCustomServiceWrapper { TestCustomServiceWrapper {
handler: pus17_handler, handler: pus17_handler,
test_srv_event_sender: event_sender, event_tx: event_sender,
} }
} }
@ -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::SyncSender<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

@ -10,9 +10,14 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
log = "0.4" log = "0.4"
thiserror = "1" thiserror = "1"
fern = "0.5"
humantime = "2"
[dependencies.asynchronix] [dependencies.asynchronix]
version = "0.2.1" version = "0.2.1"
git = "https://github.com/asynchronics/asynchronix.git"
branch = "main"
features = ["serde"]
[dependencies.satrs] [dependencies.satrs]
path = "../satrs" path = "../satrs"

View File

@ -189,11 +189,11 @@ pub mod tests {
#[test] #[test]
fn test_basic_mgm_request() { fn test_basic_mgm_request() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
let request = SimRequest::new(MgmRequest::RequestSensorData); let request = SimRequest::new_with_epoch_time(MgmRequest::RequestSensorData);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
@ -212,11 +212,11 @@ pub mod tests {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
switch_device_on(&mut sim_testbench, PcduSwitch::Mgm); switch_device_on(&mut sim_testbench, PcduSwitch::Mgm);
let mut request = SimRequest::new(MgmRequest::RequestSensorData); let mut request = SimRequest::new_with_epoch_time(MgmRequest::RequestSensorData);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
let mut sim_reply_res = sim_testbench.try_receive_next_reply(); let mut sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
@ -226,11 +226,11 @@ pub mod tests {
.expect("failed to deserialize MGM sensor values"); .expect("failed to deserialize MGM sensor values");
sim_testbench.step_by(Duration::from_millis(50)); sim_testbench.step_by(Duration::from_millis(50));
request = SimRequest::new(MgmRequest::RequestSensorData); request = SimRequest::new_with_epoch_time(MgmRequest::RequestSensorData);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
sim_reply_res = sim_testbench.try_receive_next_reply(); sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
@ -245,11 +245,11 @@ pub mod tests {
#[test] #[test]
fn test_basic_mgt_request_is_off() { fn test_basic_mgt_request_is_off() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
let request = SimRequest::new(MgtRequest::RequestHk); let request = SimRequest::new_with_epoch_time(MgtRequest::RequestHk);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply(); let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_none()); assert!(sim_reply_res.is_none());
@ -259,12 +259,12 @@ pub mod tests {
fn test_basic_mgt_request_is_on() { fn test_basic_mgt_request_is_on() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
switch_device_on(&mut sim_testbench, PcduSwitch::Mgt); switch_device_on(&mut sim_testbench, PcduSwitch::Mgt);
let request = SimRequest::new(MgtRequest::RequestHk); let request = SimRequest::new_with_epoch_time(MgtRequest::RequestHk);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply(); let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
@ -281,11 +281,11 @@ pub mod tests {
} }
fn check_mgt_hk(sim_testbench: &mut SimTestbench, expected_hk_set: MgtHkSet) { fn check_mgt_hk(sim_testbench: &mut SimTestbench, expected_hk_set: MgtHkSet) {
let request = SimRequest::new(MgtRequest::RequestHk); let request = SimRequest::new_with_epoch_time(MgtRequest::RequestHk);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
let sim_reply_res = sim_testbench.try_receive_next_reply(); let sim_reply_res = sim_testbench.try_receive_next_reply();
assert!(sim_reply_res.is_some()); assert!(sim_reply_res.is_some());
@ -309,14 +309,14 @@ pub mod tests {
y: 200, y: 200,
z: 1000, z: 1000,
}; };
let request = SimRequest::new(MgtRequest::ApplyTorque { let request = SimRequest::new_with_epoch_time(MgtRequest::ApplyTorque {
duration: Duration::from_millis(100), duration: Duration::from_millis(100),
dipole: commanded_dipole, dipole: commanded_dipole,
}); });
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step_by(Duration::from_millis(5)); sim_testbench.step_by(Duration::from_millis(5));
check_mgt_hk( check_mgt_hk(

View File

@ -49,25 +49,27 @@ impl SimController {
} }
pub fn run(&mut self, start_time: MonotonicTime, udp_polling_interval_ms: u64) { pub fn run(&mut self, start_time: MonotonicTime, udp_polling_interval_ms: u64) {
let mut t = start_time + Duration::from_millis(udp_polling_interval_ms); let mut t = start_time;
self.sys_clock.synchronize(t);
loop { loop {
let t_old = t;
// Check for UDP requests every millisecond. Shift the simulator ahead here to prevent // Check for UDP requests every millisecond. Shift the simulator ahead here to prevent
// replies lying in the past. // replies lying in the past.
t += Duration::from_millis(udp_polling_interval_ms); t += Duration::from_millis(udp_polling_interval_ms);
self.sys_clock.synchronize(t);
self.handle_sim_requests(t_old);
self.simulation self.simulation
.step_until(t) .step_until(t)
.expect("simulation step failed"); .expect("simulation step failed");
self.handle_sim_requests();
self.sys_clock.synchronize(t);
} }
} }
pub fn handle_sim_requests(&mut self) { pub fn handle_sim_requests(&mut self, old_timestamp: MonotonicTime) {
loop { loop {
match self.request_receiver.try_recv() { match self.request_receiver.try_recv() {
Ok(request) => { Ok(request) => {
if request.timestamp < old_timestamp {
log::warn!("stale data with timestamp {:?} received", request.timestamp);
}
if let Err(e) = match request.target() { if let Err(e) = match request.target() {
SimTarget::SimCtrl => self.handle_ctrl_request(&request), SimTarget::SimCtrl => self.handle_ctrl_request(&request),
SimTarget::Mgm => self.handle_mgm_request(&request), SimTarget::Mgm => self.handle_mgm_request(&request),
@ -172,11 +174,11 @@ mod tests {
#[test] #[test]
fn test_basic_ping() { fn test_basic_ping() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
let request = SimRequest::new(SimCtrlRequest::Ping); let request = SimRequest::new_with_epoch_time(SimCtrlRequest::Ping);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending sim ctrl request failed"); .expect("sending sim ctrl request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());

View File

@ -86,14 +86,14 @@ pub(crate) mod tests {
switch: PcduSwitch, switch: PcduSwitch,
target: SwitchStateBinary, target: SwitchStateBinary,
) { ) {
let request = SimRequest::new(PcduRequest::SwitchDevice { let request = SimRequest::new_with_epoch_time(PcduRequest::SwitchDevice {
switch, switch,
state: target, state: target,
}); });
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM switch request failed"); .expect("sending MGM switch request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
} }
@ -113,11 +113,11 @@ pub(crate) mod tests {
} }
fn check_switch_state(sim_testbench: &mut SimTestbench, expected_switch_map: &SwitchMap) { fn check_switch_state(sim_testbench: &mut SimTestbench, expected_switch_map: &SwitchMap) {
let request = SimRequest::new(PcduRequest::RequestSwitchInfo); let request = SimRequest::new_with_epoch_time(PcduRequest::RequestSwitchInfo);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step(); sim_testbench.step();
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();
assert!(sim_reply.is_some()); assert!(sim_reply.is_some());
@ -143,11 +143,11 @@ pub(crate) mod tests {
#[test] #[test]
fn test_pcdu_switcher_request() { fn test_pcdu_switcher_request() {
let mut sim_testbench = SimTestbench::new(); let mut sim_testbench = SimTestbench::new();
let request = SimRequest::new(PcduRequest::RequestSwitchInfo); let request = SimRequest::new_with_epoch_time(PcduRequest::RequestSwitchInfo);
sim_testbench sim_testbench
.send_request(request) .send_request(request)
.expect("sending MGM request failed"); .expect("sending MGM request failed");
sim_testbench.handle_sim_requests(); sim_testbench.handle_sim_requests_time_agnostic();
sim_testbench.step_by(Duration::from_millis(1)); sim_testbench.step_by(Duration::from_millis(1));
let sim_reply = sim_testbench.try_receive_next_reply(); let sim_reply = sim_testbench.try_receive_next_reply();

View File

@ -1,5 +1,8 @@
use asynchronix::time::MonotonicTime;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub const SIM_CTRL_UDP_PORT: u16 = 7303;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SimTarget { pub enum SimTarget {
SimCtrl, SimCtrl,
@ -19,6 +22,7 @@ pub struct SimMessage {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SimRequest { pub struct SimRequest {
inner: SimMessage, inner: SimMessage,
pub timestamp: MonotonicTime,
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -53,12 +57,22 @@ pub trait SimMessageProvider: Serialize + DeserializeOwned + Clone + Sized {
} }
impl SimRequest { impl SimRequest {
pub fn new<T: SerializableSimMsgPayload<SimRequest>>(serializable_request: T) -> Self { pub fn new_with_epoch_time<T: SerializableSimMsgPayload<SimRequest>>(
serializable_request: T,
) -> Self {
Self::new(serializable_request, MonotonicTime::EPOCH)
}
pub fn new<T: SerializableSimMsgPayload<SimRequest>>(
serializable_request: T,
timestamp: MonotonicTime,
) -> Self {
Self { Self {
inner: SimMessage { inner: SimMessage {
target: T::TARGET, target: T::TARGET,
payload: serde_json::to_string(&serializable_request).unwrap(), payload: serde_json::to_string(&serializable_request).unwrap(),
}, },
timestamp,
} }
} }
} }
@ -363,7 +377,7 @@ pub mod tests {
#[test] #[test]
fn test_basic_request() { fn test_basic_request() {
let sim_request = SimRequest::new(DummyRequest::Ping); let sim_request = SimRequest::new_with_epoch_time(DummyRequest::Ping);
assert_eq!(sim_request.target(), SimTarget::SimCtrl); assert_eq!(sim_request.target(), SimTarget::SimCtrl);
assert_eq!(sim_request.msg_type(), SimMessageType::Request); assert_eq!(sim_request.msg_type(), SimMessageType::Request);
let dummy_request = let dummy_request =

View File

@ -3,7 +3,7 @@ use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::{MonotonicTime, SystemClock}; use asynchronix::time::{MonotonicTime, SystemClock};
use controller::SimController; use controller::SimController;
use eps::PcduModel; use eps::PcduModel;
use satrs_minisim::{SimReply, SimRequest}; use satrs_minisim::{SimReply, SimRequest, SIM_CTRL_UDP_PORT};
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
@ -83,14 +83,38 @@ fn main() {
let t0 = MonotonicTime::EPOCH; let t0 = MonotonicTime::EPOCH;
let mut sim_ctrl = let mut sim_ctrl =
create_sim_controller(ThreadingModel::Default, t0, reply_sender, request_receiver); create_sim_controller(ThreadingModel::Default, t0, reply_sender, request_receiver);
// Configure logger at runtime
fern::Dispatch::new()
// Perform allocation-free log formatting
.format(|out, message, record| {
out.finish(format_args!(
"[{} {} {}] {}",
humantime::format_rfc3339(std::time::SystemTime::now()),
record.level(),
record.target(),
message
))
})
// Add blanket level filter -
.level(log::LevelFilter::Debug)
// - and per-module overrides
// Output to stdout, files, and other Dispatch configurations
.chain(std::io::stdout())
.chain(fern::log_file("output.log").expect("could not open log output file"))
// Apply globally
.apply()
.expect("could not apply logger configuration");
log::info!("starting simulation thread");
// This thread schedules the simulator. // This thread schedules the simulator.
let sim_thread = thread::spawn(move || { let sim_thread = thread::spawn(move || {
sim_ctrl.run(t0, 1); sim_ctrl.run(t0, 1);
}); });
let mut udp_server = SimUdpServer::new(0, request_sender, reply_receiver, 200, None) let mut udp_server =
.expect("could not create UDP request server"); SimUdpServer::new(SIM_CTRL_UDP_PORT, request_sender, reply_receiver, 200, None)
.expect("could not create UDP request server");
log::info!("starting UDP server on port {}", SIM_CTRL_UDP_PORT);
// This thread manages the simulator UDP server. // This thread manages the simulator UDP server.
let udp_tc_thread = thread::spawn(move || { let udp_tc_thread = thread::spawn(move || {
udp_server.run(); udp_server.run();

View File

@ -26,10 +26,13 @@ impl SimTestbench {
request_sender, request_sender,
} }
} }
pub fn handle_sim_requests_time_agnostic(&mut self) {
self.handle_sim_requests(MonotonicTime::EPOCH);
}
delegate! { delegate! {
to self.sim_controller { to self.sim_controller {
pub fn handle_sim_requests(&mut self); pub fn handle_sim_requests(&mut self, old_timestamp: MonotonicTime);
} }
to self.sim_controller.simulation { to self.sim_controller.simulation {
pub fn step(&mut self); pub fn step(&mut self);

View File

@ -270,7 +270,7 @@ mod tests {
UdpTestbench::new(true, Some(SERVER_WAIT_TIME_MS), 10) UdpTestbench::new(true, Some(SERVER_WAIT_TIME_MS), 10)
.expect("could not create testbench"); .expect("could not create testbench");
let server_thread = std::thread::spawn(move || udp_server.run()); let server_thread = std::thread::spawn(move || udp_server.run());
let sim_request = SimRequest::new(PcduRequest::RequestSwitchInfo); let sim_request = SimRequest::new_with_epoch_time(PcduRequest::RequestSwitchInfo);
udp_testbench udp_testbench
.send_request(&sim_request) .send_request(&sim_request)
.expect("sending request failed"); .expect("sending request failed");
@ -292,7 +292,7 @@ mod tests {
.expect("could not create testbench"); .expect("could not create testbench");
let server_thread = std::thread::spawn(move || udp_server.run()); let server_thread = std::thread::spawn(move || udp_server.run());
udp_testbench udp_testbench
.send_request(&SimRequest::new(SimCtrlRequest::Ping)) .send_request(&SimRequest::new_with_epoch_time(SimCtrlRequest::Ping))
.expect("sending request failed"); .expect("sending request failed");
let sim_reply = SimReply::new(PcduReply::SwitchInfo(get_all_off_switch_map())); let sim_reply = SimReply::new(PcduReply::SwitchInfo(get_all_off_switch_map()));
@ -316,7 +316,7 @@ mod tests {
// Send a ping so that the server knows the address of the client. // Send a ping so that the server knows the address of the client.
// Do not check that the request arrives on the receiver side, is done by other test. // Do not check that the request arrives on the receiver side, is done by other test.
udp_testbench udp_testbench
.send_request(&SimRequest::new(SimCtrlRequest::Ping)) .send_request(&SimRequest::new_with_epoch_time(SimCtrlRequest::Ping))
.expect("sending request failed"); .expect("sending request failed");
// Send a reply to the server, ensure it gets forwarded to the client. // Send a reply to the server, ensure it gets forwarded to the client.
@ -347,7 +347,7 @@ mod tests {
// Connect by sending a ping. // Connect by sending a ping.
udp_testbench udp_testbench
.send_request(&SimRequest::new(SimCtrlRequest::Ping)) .send_request(&SimRequest::new_with_epoch_time(SimCtrlRequest::Ping))
.expect("sending request failed"); .expect("sending request failed");
std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS)); std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS));
@ -376,7 +376,7 @@ mod tests {
// Connect by sending a ping. // Connect by sending a ping.
udp_testbench udp_testbench
.send_request(&SimRequest::new(SimCtrlRequest::Ping)) .send_request(&SimRequest::new_with_epoch_time(SimCtrlRequest::Ping))
.expect("sending request failed"); .expect("sending request failed");
std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS)); std::thread::sleep(Duration::from_millis(SERVER_WAIT_TIME_MS));

View File

@ -8,6 +8,36 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
## Changed
- Renamed `StaticPoolConfig::new` to `StaticPoolConfig::new_from_subpool_cfg_tuples`. The new
`new` implementation expects a type struct instead of tuples.
## Added
- `StaticHeaplessMemoryPool` which can be grown with user-provided static buffers.
# [v0.2.1] 2024-05-19
## Changed
- The HAL TCP server `ServerConfig::new` method now sets the `reuse_port` and `reuse_addr`
fields to `true`.
## Fixed
- Possibly subtly broken v0.2.0 build artifact.
# [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 # [v0.2.0-rc.5] 2024-04-24
## Added ## Added

View File

@ -1,6 +1,6 @@
[package] [package]
name = "satrs" name = "satrs"
version = "0.2.0-rc.5" version = "0.2.1"
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"

View File

@ -21,11 +21,11 @@
//! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo}; //! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo};
//! //!
//! const MSG_RECVD: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(1, 0); //! const MSG_RECVD: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::new(1, 0);
//! const MSG_FAILED: EventU32 = EventU32::new(Severity::LOW, 1, 1); //! const MSG_FAILED: EventU32 = EventU32::new(Severity::Low, 1, 1);
//! //!
//! const TEMPERATURE_HIGH: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::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;

View File

@ -66,8 +66,8 @@ impl ServerConfig {
inner_loop_delay, inner_loop_delay,
tm_buffer_size, tm_buffer_size,
tc_buffer_size, tc_buffer_size,
reuse_addr: false, reuse_addr: true,
reuse_port: false, reuse_port: true,
} }
} }
} }

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]

File diff suppressed because it is too large Load Diff

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

@ -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);

View File

@ -50,12 +50,6 @@ pub mod heapless_mod {
phantom: PhantomData<Provider>, phantom: PhantomData<Provider>,
} }
/// Safety: All contained field are [Send] as well
unsafe impl<const N: usize, Event: GenericEvent + Send> Send
for HeaplessPusMgmtBackendProvider<N, Event>
{
}
impl<const N: usize, Provider: GenericEvent> PusEventReportingMapProvider<Provider> impl<const N: usize, Provider: GenericEvent> PusEventReportingMapProvider<Provider>
for HeaplessPusMgmtBackendProvider<N, Provider> for HeaplessPusMgmtBackendProvider<N, Provider>
{ {
@ -107,6 +101,7 @@ pub mod alloc_mod {
use crate::{ use crate::{
events::EventU16, events::EventU16,
params::{Params, WritableToBeBytes},
pus::event::{DummyEventHook, EventTmHookProvider}, pus::event::{DummyEventHook, EventTmHookProvider},
}; };
@ -147,6 +142,12 @@ pub mod alloc_mod {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct EventGenerationResult {
pub event_was_enabled: bool,
pub params_were_propagated: bool,
}
pub struct PusEventTmCreatorWithMap< pub struct PusEventTmCreatorWithMap<
ReportingMap: PusEventReportingMapProvider<Event>, ReportingMap: PusEventReportingMapProvider<Event>,
Event: GenericEvent, Event: GenericEvent,
@ -212,6 +213,53 @@ pub mod alloc_mod {
.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<Event: GenericEvent + Copy + PartialEq + Eq + Hash, EventTmHook: EventTmHookProvider> impl<Event: GenericEvent + Copy + PartialEq + Eq + Hash, EventTmHook: EventTmHookProvider>
@ -261,6 +309,12 @@ pub mod alloc_mod {
} }
#[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};
@ -336,4 +390,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,7 +170,7 @@ mod tests {
event_man::EventRequestWithToken, event_man::EventRequestWithToken,
tests::PusServiceHandlerWithSharedStoreCommon, tests::PusServiceHandlerWithSharedStoreCommon,
verification::{TcStateAccepted, VerificationToken}, verification::{TcStateAccepted, VerificationToken},
EcssTcInSharedStoreConverter, PusPacketHandlerResult, PusPacketHandlingError, DirectPusPacketHandlerResult, EcssTcInSharedStoreConverter, PusPacketHandlingError,
}, },
}; };
@ -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(),
} }
} }
} }
@ -1341,7 +1356,10 @@ pub mod tests {
/// ///
/// The PUS service handler is instantiated with a [EcssTcInStoreConverter]. /// The PUS service handler is instantiated with a [EcssTcInStoreConverter].
pub fn new(id: ComponentId) -> (Self, PusServiceHelperStatic) { pub fn new(id: ComponentId) -> (Self, PusServiceHelperStatic) {
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false); let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
alloc::vec![(16, 16), (8, 32), (4, 64)],
false,
);
let tc_pool = StaticMemoryPool::new(pool_cfg.clone()); let tc_pool = StaticMemoryPool::new(pool_cfg.clone());
let tm_pool = StaticMemoryPool::new(pool_cfg); let tm_pool = StaticMemoryPool::new(pool_cfg);
let shared_tc_pool = SharedStaticMemoryPool::new(RwLock::new(tc_pool)); let shared_tc_pool = SharedStaticMemoryPool::new(RwLock::new(tc_pool));

View File

@ -939,7 +939,10 @@ mod tests {
#[test] #[test]
fn test_reset() { fn test_reset() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
@ -1088,7 +1091,10 @@ mod tests {
} }
#[test] #[test]
fn test_release_telecommands() { fn test_release_telecommands() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
@ -1153,7 +1159,10 @@ mod tests {
#[test] #[test]
fn release_multi_with_same_time() { fn release_multi_with_same_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
@ -1210,7 +1219,10 @@ mod tests {
#[test] #[test]
fn release_with_scheduler_disabled() { fn release_with_scheduler_disabled() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
scheduler.disable(); scheduler.disable();
@ -1278,7 +1290,10 @@ mod tests {
fn insert_unwrapped_tc() { fn insert_unwrapped_tc() {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
@ -1325,7 +1340,10 @@ mod tests {
fn insert_wrapped_tc() { fn insert_wrapped_tc() {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc = scheduled_tc(UnixTime::new_only_secs(100), &mut buf); let tc = scheduled_tc(UnixTime::new_only_secs(100), &mut buf);
@ -1374,7 +1392,10 @@ mod tests {
fn insert_wrong_service() { fn insert_wrong_service() {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc = wrong_tc_service(UnixTime::new_only_secs(100), &mut buf); let tc = wrong_tc_service(UnixTime::new_only_secs(100), &mut buf);
@ -1396,7 +1417,10 @@ mod tests {
fn insert_wrong_subservice() { fn insert_wrong_subservice() {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc = wrong_tc_subservice(UnixTime::new_only_secs(100), &mut buf); let tc = wrong_tc_subservice(UnixTime::new_only_secs(100), &mut buf);
@ -1417,7 +1441,10 @@ mod tests {
#[test] #[test]
fn insert_wrapped_tc_faulty_app_data() { fn insert_wrapped_tc_faulty_app_data() {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let tc = invalid_time_tagged_cmd(); let tc = invalid_time_tagged_cmd();
let insert_res = scheduler.insert_wrapped_tc::<cds::CdsTime>(&tc, &mut pool); let insert_res = scheduler.insert_wrapped_tc::<cds::CdsTime>(&tc, &mut pool);
assert!(insert_res.is_err()); assert!(insert_res.is_err());
@ -1431,7 +1458,10 @@ mod tests {
#[test] #[test]
fn insert_doubly_wrapped_time_tagged_cmd() { fn insert_doubly_wrapped_time_tagged_cmd() {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut buf: [u8; 64] = [0; 64]; let mut buf: [u8; 64] = [0; 64];
let tc = double_wrapped_time_tagged_tc(UnixTime::new_only_secs(50), &mut buf); let tc = double_wrapped_time_tagged_tc(UnixTime::new_only_secs(50), &mut buf);
let insert_res = scheduler.insert_wrapped_tc::<cds::CdsTime>(&tc, &mut pool); let insert_res = scheduler.insert_wrapped_tc::<cds::CdsTime>(&tc, &mut pool);
@ -1465,7 +1495,10 @@ mod tests {
fn release_time_within_time_margin() { fn release_time_within_time_margin() {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
@ -1489,7 +1522,10 @@ mod tests {
#[test] #[test]
fn test_store_error_propagation_release() { fn test_store_error_propagation_release() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
@ -1523,7 +1559,10 @@ mod tests {
#[test] #[test]
fn test_store_error_propagation_reset() { fn test_store_error_propagation_reset() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
@ -1546,7 +1585,10 @@ mod tests {
#[test] #[test]
fn test_delete_by_req_id_simple_retrieve_addr() { fn test_delete_by_req_id_simple_retrieve_addr() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
@ -1564,7 +1606,10 @@ mod tests {
#[test] #[test]
fn test_delete_by_req_id_simple_delete_all() { fn test_delete_by_req_id_simple_delete_all() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
@ -1582,7 +1627,10 @@ mod tests {
#[test] #[test]
fn test_delete_by_req_id_complex() { fn test_delete_by_req_id_complex() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]); let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
@ -1627,7 +1675,10 @@ mod tests {
fn insert_full_store_test() { fn insert_full_store_test() {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(1, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(1, 64)],
false,
));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
// Store is full after this. // Store is full after this.
@ -1663,7 +1714,10 @@ mod tests {
#[test] #[test]
fn test_time_window_retrieval_select_all() { fn test_time_window_retrieval_select_all() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
@ -1692,7 +1746,10 @@ mod tests {
#[test] #[test]
fn test_time_window_retrieval_select_from_stamp() { fn test_time_window_retrieval_select_from_stamp() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
@ -1724,7 +1781,10 @@ mod tests {
#[test] #[test]
fn test_time_window_retrieval_select_to_time() { fn test_time_window_retrieval_select_to_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
@ -1756,7 +1816,10 @@ mod tests {
#[test] #[test]
fn test_time_window_retrieval_select_from_time_to_time() { fn test_time_window_retrieval_select_from_time_to_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let tc_info_1 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
@ -1794,7 +1857,10 @@ mod tests {
#[test] #[test]
fn test_deletion_all() { fn test_deletion_all() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
@ -1820,7 +1886,10 @@ mod tests {
#[test] #[test]
fn test_deletion_from_start_time() { fn test_deletion_from_start_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
@ -1842,7 +1911,10 @@ mod tests {
#[test] #[test]
fn test_deletion_to_end_time() { fn test_deletion_to_end_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let cmd_1_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
@ -1865,7 +1937,10 @@ mod tests {
#[test] #[test]
fn test_deletion_from_start_time_to_end_time() { fn test_deletion_from_start_time_to_end_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let cmd_out_of_range_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50); let cmd_out_of_range_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100); let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 100);
@ -1897,7 +1972,10 @@ mod tests {
#[test] #[test]
fn test_release_without_deletion() { fn test_release_without_deletion() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false)); let mut pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(10, 32), (5, 64)],
false,
));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5)); let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];

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;
@ -288,7 +286,10 @@ mod tests {
impl Pus11HandlerWithStoreTester { impl Pus11HandlerWithStoreTester {
pub fn new() -> Self { pub fn new() -> Self {
let test_scheduler = TestScheduler::default(); let test_scheduler = TestScheduler::default();
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false); let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
alloc::vec![(16, 16), (8, 32), (4, 64)],
false,
);
let sched_tc_pool = StaticMemoryPool::new(pool_cfg.clone()); let sched_tc_pool = StaticMemoryPool::new(pool_cfg.clone());
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(0); let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(0);
Self { Self {
@ -298,10 +299,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 +390,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

@ -31,7 +31,9 @@
//! const TEST_APID: u16 = 0x02; //! const TEST_APID: u16 = 0x02;
//! const TEST_COMPONENT_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); //! const TEST_COMPONENT_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
//! //!
//! let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false); //! let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
//! vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false
//! );
//! let tm_pool = StaticMemoryPool::new(pool_cfg.clone()); //! let tm_pool = StaticMemoryPool::new(pool_cfg.clone());
//! let shared_tm_pool = SharedStaticMemoryPool::new(RwLock::new(tm_pool)); //! let shared_tm_pool = SharedStaticMemoryPool::new(RwLock::new(tm_pool));
//! let (verif_tx, verif_rx) = mpsc::sync_channel(10); //! let (verif_tx, verif_rx) = mpsc::sync_channel(10);
@ -79,6 +81,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 +356,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 +384,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 +1174,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_generic_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 +1686,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 +1706,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 +1721,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 +1731,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 +1809,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 +1913,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 +1973,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 +1985,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 +1998,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);
} }
} }
@ -2118,7 +2071,10 @@ pub mod tests {
#[test] #[test]
fn test_mpsc_verif_send() { fn test_mpsc_verif_send() {
let pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(8, 8)], false)); let pool = StaticMemoryPool::new(StaticPoolConfig::new_from_subpool_cfg_tuples(
vec![(8, 8)],
false,
));
let shared_tm_store = let shared_tm_store =
SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new(pool))); SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new(pool)));
let (tx, _) = mpsc::sync_channel(10); let (tx, _) = mpsc::sync_channel(10);
@ -2128,7 +2084,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 +2092,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 +2160,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 +2172,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 +2183,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 +2197,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 +2216,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 +2236,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 +2262,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 +2270,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 +2287,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 +2334,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

@ -486,7 +486,9 @@ pub(crate) mod tests {
use std::sync::RwLock; use std::sync::RwLock;
use crate::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig}; use crate::pool::{
PoolProviderWithGuards, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig,
};
use super::*; use super::*;
use std::sync::mpsc; use std::sync::mpsc;
@ -554,7 +556,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_basic_shared_store_sender_unbounded_sender() { fn test_basic_shared_store_sender_unbounded_sender() {
let (tc_tx, tc_rx) = mpsc::channel(); let (tc_tx, tc_rx) = mpsc::channel();
let pool_cfg = StaticPoolConfig::new(vec![(2, 8)], true); let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(vec![(2, 8)], true);
let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new( let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new(
StaticMemoryPool::new(pool_cfg), StaticMemoryPool::new(pool_cfg),
))); )));
@ -571,7 +573,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_basic_shared_store_sender() { fn test_basic_shared_store_sender() {
let (tc_tx, tc_rx) = mpsc::sync_channel(10); let (tc_tx, tc_rx) = mpsc::sync_channel(10);
let pool_cfg = StaticPoolConfig::new(vec![(2, 8)], true); let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(vec![(2, 8)], true);
let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new( let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new(
StaticMemoryPool::new(pool_cfg), StaticMemoryPool::new(pool_cfg),
))); )));
@ -588,7 +590,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_basic_shared_store_sender_rx_dropped() { fn test_basic_shared_store_sender_rx_dropped() {
let (tc_tx, tc_rx) = mpsc::sync_channel(10); let (tc_tx, tc_rx) = mpsc::sync_channel(10);
let pool_cfg = StaticPoolConfig::new(vec![(2, 8)], true); let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(vec![(2, 8)], true);
let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new( let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new(
StaticMemoryPool::new(pool_cfg), StaticMemoryPool::new(pool_cfg),
))); )));
@ -606,7 +608,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_basic_shared_store_sender_queue_full() { fn test_basic_shared_store_sender_queue_full() {
let (tc_tx, tc_rx) = mpsc::sync_channel(1); let (tc_tx, tc_rx) = mpsc::sync_channel(1);
let pool_cfg = StaticPoolConfig::new(vec![(2, 8)], true); let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(vec![(2, 8)], true);
let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new( let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new(
StaticMemoryPool::new(pool_cfg), StaticMemoryPool::new(pool_cfg),
))); )));
@ -629,7 +631,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_basic_shared_store_store_error() { fn test_basic_shared_store_store_error() {
let (tc_tx, tc_rx) = mpsc::sync_channel(1); let (tc_tx, tc_rx) = mpsc::sync_channel(1);
let pool_cfg = StaticPoolConfig::new(vec![(1, 8)], true); let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(vec![(1, 8)], true);
let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new( let shared_pool = SharedPacketPool::new(&SharedStaticMemoryPool::new(RwLock::new(
StaticMemoryPool::new(pool_cfg), StaticMemoryPool::new(pool_cfg),
))); )));

View File

@ -9,7 +9,8 @@ const DUMMY_DATA: [u8; 4] = [0, 1, 2, 3];
#[test] #[test]
fn threaded_usage() { fn threaded_usage() {
let pool_cfg = StaticPoolConfig::new(vec![(16, 6), (32, 3), (8, 12)], false); let pool_cfg =
StaticPoolConfig::new_from_subpool_cfg_tuples(vec![(16, 6), (32, 3), (8, 12)], false);
let shared_pool = Arc::new(RwLock::new(StaticMemoryPool::new(pool_cfg))); let shared_pool = Arc::new(RwLock::new(StaticMemoryPool::new(pool_cfg)));
let shared_clone = shared_pool.clone(); let shared_clone = shared_pool.clone();
let (tx, rx): (Sender<PoolAddr>, Receiver<PoolAddr>) = mpsc::channel(); let (tx, rx): (Sender<PoolAddr>, Receiver<PoolAddr>) = mpsc::channel();

View File

@ -33,8 +33,10 @@ pub mod crossbeam_test {
// each reporter have an own sequence count provider. // each reporter have an own sequence count provider.
let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
// Shared pool object to store the verification PUS telemetry // Shared pool object to store the verification PUS telemetry
let pool_cfg = let pool_cfg = StaticPoolConfig::new_from_subpool_cfg_tuples(
StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false); vec![(10, 32), (10, 64), (10, 128), (10, 1024)],
false,
);
let shared_tm_pool = let shared_tm_pool =
SharedStaticMemoryPool::new(RwLock::new(StaticMemoryPool::new(pool_cfg.clone()))); SharedStaticMemoryPool::new(RwLock::new(StaticMemoryPool::new(pool_cfg.clone())));
let shared_tc_pool = let shared_tc_pool =