From 86bdf35ca3a2a47658bd9e70c16657df40921afc Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 19 Aug 2022 12:03:43 +0200 Subject: [PATCH 01/43] adapt test code --- fsrc-core/tests/pool_test.rs | 79 +++++++++++++----------------------- 1 file changed, 29 insertions(+), 50 deletions(-) diff --git a/fsrc-core/tests/pool_test.rs b/fsrc-core/tests/pool_test.rs index cc6a57e..54e10e9 100644 --- a/fsrc-core/tests/pool_test.rs +++ b/fsrc-core/tests/pool_test.rs @@ -1,20 +1,21 @@ use std::sync::{Arc, RwLock}; use std::thread; +use fsrc_core::pool::{LocalPool, PoolCfg, StoreAddr, StoreError}; -struct PoolDummy { - test_buf: [u8; 128], -} struct PoolAccessDummy<'a> { - pool_dummy: &'a mut PoolDummy, + pool: &'a mut LocalPool, + addr: Option, no_deletion: bool, } impl PoolAccessDummy<'_> { - fn modify(&mut self) -> &mut [u8] { - self.pool_dummy.modify() + #[allow(dead_code)] + fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError> { + self.pool.modify(addr) } + #[allow(dead_code)] fn release(&mut self) { self.no_deletion = true; } @@ -25,67 +26,45 @@ impl Drop for PoolAccessDummy<'_> { if self.no_deletion { println!("Pool access: Drop with no deletion") } else { - self.pool_dummy.delete(); + if let Some(addr) = self.addr { + let res = self.pool.delete(addr); + if res.is_err() { + println!("Pool access: Deletion failed"); + } + } println!("Pool access: Drop with deletion"); } } } -impl Default for PoolDummy { - fn default() -> Self { - PoolDummy { test_buf: [0; 128] } - } -} - -impl PoolDummy { - fn modify(&mut self) -> &mut [u8] { - self.test_buf.as_mut_slice() - } - - fn modify_with_accessor(&mut self) -> PoolAccessDummy { - PoolAccessDummy { - pool_dummy: self, - no_deletion: false, - } - } - - fn read(&self) -> &[u8] { - self.test_buf.as_slice() - } - - fn delete(&mut self) { - println!("Store content was deleted"); - } -} - -fn pool_test() { +fn main() { println!("Hello World"); - let shared_dummy = Arc::new(RwLock::new(PoolDummy::default())); - let shared_clone = shared_dummy.clone(); + let pool_cfg = PoolCfg::new(vec![(16, 6), (32, 3), (8, 12)]); + let shared_dummy = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); + let _shared_clone = shared_dummy.clone(); let jh0 = thread::spawn(move || loop { { let mut dummy = shared_dummy.write().unwrap(); - let buf = dummy.modify(); - buf[0] = 1; + let dummy_data = [0, 1, 2, 3]; + let _addr = dummy.add(&dummy_data).expect("Writing data failed"); - let mut accessor = dummy.modify_with_accessor(); - let buf = accessor.modify(); - buf[0] = 2; + // let mut accessor = dummy.modify_with_accessor(); + // let buf = accessor.modify(); } }); let jh1 = thread::spawn(move || loop { { - let dummy = shared_clone.read().unwrap(); - let buf = dummy.read(); - println!("Buffer 0: {:?}", buf[0]); + // let dummy = shared_clone.read().unwrap(); + // let buf = dummy.read(); + // println!("Buffer 0: {:?}", buf[0]); } - let mut dummy = shared_clone.write().unwrap(); - let mut accessor = dummy.modify_with_accessor(); - let buf = accessor.modify(); - buf[0] = 3; - accessor.release(); + //let mut dummy = shared_clone.write().unwrap(); + //let mut accessor = dummy.modify_with_accessor(); + //let buf = accessor.modify(); + //buf[0] = 3; + //accessor.release(); }); jh0.join().unwrap(); jh1.join().unwrap(); From 6ab691e87c2b8896cfadc656008a3cb72c14661c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Aug 2022 00:28:29 +0200 Subject: [PATCH 02/43] basic TMTC infastructure for example --- .../Run_obsw_client_example.xml | 19 ++++ .idea/runConfigurations/Run_obsw_example.xml | 19 ++++ Cargo.lock | 1 + fsrc-core/src/hal/host/udp_server.rs | 14 ++- fsrc-core/src/hal/mod.rs | 2 +- fsrc-core/src/tmtc/ccsds_distrib.rs | 40 ++++--- fsrc-core/src/tmtc/mod.rs | 3 + fsrc-core/src/tmtc/pus_distrib.rs | 7 +- fsrc-core/tests/pool_test.rs | 3 +- fsrc-example/Cargo.toml | 3 + fsrc-example/src/bin/client.rs | 2 +- fsrc-example/src/bin/obsw.rs | 103 ++++++++++++++++-- spacepackets | 2 +- 13 files changed, 179 insertions(+), 39 deletions(-) create mode 100644 .idea/runConfigurations/Run_obsw_client_example.xml create mode 100644 .idea/runConfigurations/Run_obsw_example.xml diff --git a/.idea/runConfigurations/Run_obsw_client_example.xml b/.idea/runConfigurations/Run_obsw_client_example.xml new file mode 100644 index 0000000..dc85ad6 --- /dev/null +++ b/.idea/runConfigurations/Run_obsw_client_example.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run_obsw_example.xml b/.idea/runConfigurations/Run_obsw_example.xml new file mode 100644 index 0000000..ed323c9 --- /dev/null +++ b/.idea/runConfigurations/Run_obsw_example.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 455bb58..af38bfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,6 +235,7 @@ dependencies = [ name = "fsrc-example" version = "0.1.0" dependencies = [ + "fsrc-core", "spacepackets", ] diff --git a/fsrc-core/src/hal/host/udp_server.rs b/fsrc-core/src/hal/host/udp_server.rs index 53a67c5..b2c4b40 100644 --- a/fsrc-core/src/hal/host/udp_server.rs +++ b/fsrc-core/src/hal/host/udp_server.rs @@ -1,20 +1,24 @@ use crate::hal::host::udp_server::ReceiveResult::{IoError, ReceiverError}; use crate::tmtc::ReceivesTc; +use std::boxed::Box; use std::net::{SocketAddr, ToSocketAddrs, UdpSocket}; +use std::vec; use std::vec::Vec; -pub struct UdpTmtcServer { - socket: UdpSocket, +pub struct UdpTcServer { + pub socket: UdpSocket, recv_buf: Vec, + sender_addr: Option, tc_receiver: Box>, } +#[derive(Debug)] pub enum ReceiveResult { IoError(std::io::Error), ReceiverError(E), } -impl UdpTmtcServer { +impl UdpTcServer { pub fn new( addr: A, max_recv_size: usize, @@ -22,7 +26,8 @@ impl UdpTmtcServer { ) -> Result { Ok(Self { socket: UdpSocket::bind(addr)?, - recv_buf: Vec::with_capacity(max_recv_size), + recv_buf: vec![0; max_recv_size], + sender_addr: None, tc_receiver, }) } @@ -32,6 +37,7 @@ impl UdpTmtcServer { .socket .recv_from(&mut self.recv_buf) .map_err(|e| IoError(e))?; + self.sender_addr = Some(res.1); self.tc_receiver .pass_tc(&self.recv_buf[0..res.0]) .map_err(|e| ReceiverError(e))?; diff --git a/fsrc-core/src/hal/mod.rs b/fsrc-core/src/hal/mod.rs index 43769ba..0c9616d 100644 --- a/fsrc-core/src/hal/mod.rs +++ b/fsrc-core/src/hal/mod.rs @@ -1,2 +1,2 @@ -#[cfg(feature = "use_std")] +#[cfg(feature = "std")] pub mod host; diff --git a/fsrc-core/src/tmtc/ccsds_distrib.rs b/fsrc-core/src/tmtc/ccsds_distrib.rs index dfe1238..15bd1bb 100644 --- a/fsrc-core/src/tmtc/ccsds_distrib.rs +++ b/fsrc-core/src/tmtc/ccsds_distrib.rs @@ -2,7 +2,7 @@ //! //! The routing components consist of two core components: //! 1. [CcsdsDistributor] component which dispatches received packets to a user-provided handler -//! 2. [ApidPacketHandler] trait which should be implemented by the user-provided packet handler. +//! 2. [CcsdsPacketHandler] trait which should be implemented by the user-provided packet handler. //! //! The [CcsdsDistributor] implements the [ReceivesCcsdsTc] and [ReceivesTc] trait which allows to //! pass raw or CCSDS packets to it. Upon receiving a packet, it performs the following steps: @@ -11,14 +11,14 @@ //! respective CCSDS space packet header field. If that process fails, a [PacketError] is //! returned to the user //! 2. If a valid APID is found and matches one of the APIDs provided by -//! [ApidPacketHandler::valid_apids], it will pass the packet to the user provided -//! [ApidPacketHandler::handle_known_apid] function. If no valid APID is found, the packet -//! will be passed to the [ApidPacketHandler::handle_unknown_apid] function. +//! [CcsdsPacketHandler::valid_apids], it will pass the packet to the user provided +//! [CcsdsPacketHandler::handle_known_apid] function. If no valid APID is found, the packet +//! will be passed to the [CcsdsPacketHandler::handle_unknown_apid] function. //! //! # Example //! //! ```rust -//! use fsrc_core::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor}; +//! use fsrc_core::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor}; //! use fsrc_core::tmtc::ReceivesTc; //! use spacepackets::{CcsdsPacket, SpHeader}; //! use spacepackets::tc::PusTc; @@ -33,7 +33,7 @@ //! fn mutable_foo(&mut self) {} //! } //! -//! impl ApidPacketHandler for ConcreteApidHandler { +//! impl CcsdsPacketHandler for ConcreteApidHandler { //! type Error = (); //! fn valid_apids(&self) -> &'static [u16] { &[0x002] } //! fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> { @@ -86,7 +86,7 @@ //! ``` use crate::tmtc::{ReceivesCcsdsTc, ReceivesTc}; use downcast_rs::Downcast; -use spacepackets::{CcsdsPacket, PacketError, SpHeader}; +use spacepackets::{CcsdsPacket, PacketError, SizeMissmatch, SpHeader}; /// Generic trait for a handler or dispatcher object handling CCSDS packets. /// @@ -98,7 +98,7 @@ use spacepackets::{CcsdsPacket, PacketError, SpHeader}; /// This trait automatically implements the [downcast_rs::Downcast] to allow a more convenient API /// to cast trait objects back to their concrete type after the handler was passed to the /// distributor. -pub trait ApidPacketHandler: Downcast { +pub trait CcsdsPacketHandler: Downcast { type Error; fn valid_apids(&self) -> &'static [u16]; @@ -111,14 +111,14 @@ pub trait ApidPacketHandler: Downcast { ) -> Result<(), Self::Error>; } -downcast_rs::impl_downcast!(ApidPacketHandler assoc Error); +downcast_rs::impl_downcast!(CcsdsPacketHandler assoc Error); /// The CCSDS distributor dispatches received CCSDS packets to a user provided packet handler. pub struct CcsdsDistributor { /// User provided APID handler stored as a generic trait object. /// It can be cast back to the original concrete type using the [Self::apid_handler_ref] or /// the [Self::apid_handler_mut] method. - pub apid_handler: Box>, + pub apid_handler: Box>, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -139,26 +139,34 @@ impl ReceivesTc for CcsdsDistributor { type Error = CcsdsError; fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { + if tc_raw.len() < 7 { + return Err(CcsdsError::PacketError( + PacketError::FromBytesSliceTooSmall(SizeMissmatch { + found: tc_raw.len(), + expected: 7, + }), + )); + } let sp_header = SpHeader::from_raw_slice(tc_raw).map_err(|e| CcsdsError::PacketError(e))?; self.dispatch_ccsds(&sp_header, tc_raw) } } impl CcsdsDistributor { - pub fn new(apid_handler: Box>) -> Self { + pub fn new(apid_handler: Box>) -> Self { CcsdsDistributor { apid_handler } } /// This function can be used to retrieve a reference to the concrete instance of the APID /// handler after it was passed to the distributor. See the /// [module documentation][crate::tmtc::ccsds_distrib] for an fsrc-example. - pub fn apid_handler_ref>(&self) -> Option<&T> { + pub fn apid_handler_ref>(&self) -> Option<&T> { self.apid_handler.downcast_ref::() } /// This function can be used to retrieve a mutable reference to the concrete instance of the /// APID handler after it was passed to the distributor. - pub fn apid_handler_mut>(&mut self) -> Option<&mut T> { + pub fn apid_handler_mut>(&mut self) -> Option<&mut T> { self.apid_handler.downcast_mut::() } @@ -182,7 +190,7 @@ impl CcsdsDistributor { #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor}; + use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler}; use spacepackets::tc::PusTc; use spacepackets::CcsdsPacket; use std::collections::VecDeque; @@ -207,7 +215,7 @@ pub(crate) mod tests { pub unknown_packet_queue: VecDeque<(u16, Vec)>, } - impl ApidPacketHandler for BasicApidHandlerSharedQueue { + impl CcsdsPacketHandler for BasicApidHandlerSharedQueue { type Error = (); fn valid_apids(&self) -> &'static [u16] { &[0x000, 0x002] @@ -242,7 +250,7 @@ pub(crate) mod tests { } } - impl ApidPacketHandler for BasicApidHandlerOwnedQueue { + impl CcsdsPacketHandler for BasicApidHandlerOwnedQueue { type Error = (); fn valid_apids(&self) -> &'static [u16] { diff --git a/fsrc-core/src/tmtc/mod.rs b/fsrc-core/src/tmtc/mod.rs index 0c9c02c..7d52337 100644 --- a/fsrc-core/src/tmtc/mod.rs +++ b/fsrc-core/src/tmtc/mod.rs @@ -12,6 +12,9 @@ use spacepackets::SpHeader; pub mod ccsds_distrib; pub mod pus_distrib; +pub use ccsds_distrib::{CcsdsDistributor, CcsdsError, CcsdsPacketHandler}; +pub use pus_distrib::{PusDistributor, PusServiceProvider}; + const _RAW_PACKET_ERROR: &str = "raw-tmtc"; const _CCSDS_ERROR: &str = "ccsds-tmtc"; const _PUS_ERROR: &str = "pus-tmtc"; diff --git a/fsrc-core/src/tmtc/pus_distrib.rs b/fsrc-core/src/tmtc/pus_distrib.rs index 2928110..8a3dfa6 100644 --- a/fsrc-core/src/tmtc/pus_distrib.rs +++ b/fsrc-core/src/tmtc/pus_distrib.rs @@ -137,7 +137,8 @@ mod tests { use crate::tmtc::ccsds_distrib::tests::{ generate_ping_tc, BasicApidHandlerOwnedQueue, BasicApidHandlerSharedQueue, }; - use crate::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor}; + use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler}; + use alloc::vec::Vec; use spacepackets::ecss::PusError; use spacepackets::tc::PusTc; use spacepackets::CcsdsPacket; @@ -235,11 +236,11 @@ mod tests { }; } - impl ApidPacketHandler for ApidHandlerOwned { + impl CcsdsPacketHandler for ApidHandlerOwned { apid_handler_impl!(); } - impl ApidPacketHandler for ApidHandlerShared { + impl CcsdsPacketHandler for ApidHandlerShared { apid_handler_impl!(); } diff --git a/fsrc-core/tests/pool_test.rs b/fsrc-core/tests/pool_test.rs index 54e10e9..aaa7910 100644 --- a/fsrc-core/tests/pool_test.rs +++ b/fsrc-core/tests/pool_test.rs @@ -1,7 +1,6 @@ +use fsrc_core::pool::{LocalPool, PoolCfg, StoreAddr, StoreError}; use std::sync::{Arc, RwLock}; use std::thread; -use fsrc_core::pool::{LocalPool, PoolCfg, StoreAddr, StoreError}; - struct PoolAccessDummy<'a> { pool: &'a mut LocalPool, diff --git a/fsrc-example/Cargo.toml b/fsrc-example/Cargo.toml index 1ddb71a..8f29e3b 100644 --- a/fsrc-example/Cargo.toml +++ b/fsrc-example/Cargo.toml @@ -6,3 +6,6 @@ authors = ["Robin Mueller "] [dependencies.spacepackets] path = "../spacepackets" + +[dependencies.fsrc-core] +path = "../fsrc-core" diff --git a/fsrc-example/src/bin/client.rs b/fsrc-example/src/bin/client.rs index 5956570..1a71a73 100644 --- a/fsrc-example/src/bin/client.rs +++ b/fsrc-example/src/bin/client.rs @@ -8,7 +8,7 @@ fn main() { let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); let mut sph = SpHeader::tc(0x02, 0, 0).unwrap(); let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); - let client = UdpSocket::bind("127.0.0.1:7300").expect("Connecting to UDP server failed"); + let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed"); let size = pus_tc.write_to(&mut buf).expect("Creating PUS TC failed"); client .send_to(&buf[0..size], &addr) diff --git a/fsrc-example/src/bin/obsw.rs b/fsrc-example/src/bin/obsw.rs index 690cd3e..f4ae995 100644 --- a/fsrc-example/src/bin/obsw.rs +++ b/fsrc-example/src/bin/obsw.rs @@ -1,15 +1,96 @@ +use fsrc_core::hal::host::udp_server::{ReceiveResult, UdpTcServer}; +use fsrc_core::tmtc::{ + CcsdsDistributor, CcsdsError, CcsdsPacketHandler, PusDistributor, PusServiceProvider, + ReceivesCcsdsTc, +}; use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT}; -use std::net::{IpAddr, SocketAddr, UdpSocket}; +use spacepackets::tc::PusTc; +use spacepackets::{CcsdsPacket, SpHeader}; +use std::net::{IpAddr, SocketAddr}; -fn main() { - let mut recv_buf = [0; 1024]; - let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); - let socket = UdpSocket::bind(&addr).expect("Error opening UDP socket"); - loop { - let (num_bytes, src) = socket.recv_from(&mut recv_buf).expect("UDP Receive error"); - println!( - "Received TM with len {num_bytes} from {src}: {:x?}", - &recv_buf[0..num_bytes] - ); +const PUS_APID: u16 = 0x02; + +struct PusReceiver {} + +struct CcsdsReceiver { + pus_handler: PusDistributor<()>, +} + +impl CcsdsPacketHandler for CcsdsReceiver { + type Error = (); + + fn valid_apids(&self) -> &'static [u16] { + &[PUS_APID] + } + + fn handle_known_apid( + &mut self, + sp_header: &SpHeader, + tc_raw: &[u8], + ) -> Result<(), Self::Error> { + if sp_header.apid() == PUS_APID { + self.pus_handler + .pass_ccsds(sp_header, tc_raw) + .expect("Handling PUS packet failed"); + } + Ok(()) + } + + fn handle_unknown_apid( + &mut self, + _sp_header: &SpHeader, + _tc_raw: &[u8], + ) -> Result<(), Self::Error> { + println!("Unknown APID detected"); + Ok(()) + } +} + +impl PusServiceProvider for PusReceiver { + type Error = (); + + fn handle_pus_tc_packet( + &mut self, + service: u8, + _header: &SpHeader, + pus_tc: &PusTc, + ) -> Result<(), Self::Error> { + if service == 17 { + println!("Received PUS ping command"); + let raw_data = pus_tc.raw().expect("Could not retrieve raw data"); + println!("Raw data: 0x{raw_data:x?}"); + } + Ok(()) + } +} + +fn main() { + let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); + let pus_receiver = PusReceiver {}; + let pus_distributor = PusDistributor::new(Box::new(pus_receiver)); + let ccsds_receiver = CcsdsReceiver { + pus_handler: pus_distributor, + }; + let ccsds_distributor = CcsdsDistributor::new(Box::new(ccsds_receiver)); + let mut udp_tmtc_server = UdpTcServer::new(addr, 2048, Box::new(ccsds_distributor)) + .expect("Creating UDP TMTC server failed"); + loop { + let res = udp_tmtc_server.recv_tc(); + match res { + Ok(_) => (), + Err(e) => match e { + ReceiveResult::ReceiverError(e) => match e { + CcsdsError::PacketError(e) => { + println!("Got packet error: {e:?}"); + } + CcsdsError::CustomError(_) => { + println!("Unknown receiver error") + } + }, + ReceiveResult::IoError(e) => { + println!("IO error {e}"); + } + }, + } } } diff --git a/spacepackets b/spacepackets index 35073a4..42d3487 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit 35073a45a536051e3852696c501d7afa1b36a808 +Subproject commit 42d3487c1934cfecf9b2d599c2f81fde52a61cc3 From 18b417bddc30dd03800bced6811e1ed89d2b7de3 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 28 Aug 2022 01:26:13 +0200 Subject: [PATCH 03/43] continuing obsw example --- fsrc-core/src/hal/host/udp_server.rs | 38 ++++--- fsrc-example/src/bin/obsw.rs | 153 +++++++++++++++++++++------ 2 files changed, 149 insertions(+), 42 deletions(-) diff --git a/fsrc-core/src/hal/host/udp_server.rs b/fsrc-core/src/hal/host/udp_server.rs index b2c4b40..1d737d8 100644 --- a/fsrc-core/src/hal/host/udp_server.rs +++ b/fsrc-core/src/hal/host/udp_server.rs @@ -1,6 +1,6 @@ -use crate::hal::host::udp_server::ReceiveResult::{IoError, ReceiverError}; use crate::tmtc::ReceivesTc; use std::boxed::Box; +use std::io::ErrorKind; use std::net::{SocketAddr, ToSocketAddrs, UdpSocket}; use std::vec; use std::vec::Vec; @@ -14,7 +14,8 @@ pub struct UdpTcServer { #[derive(Debug)] pub enum ReceiveResult { - IoError(std::io::Error), + WouldBlock, + OtherIoError(std::io::Error), ReceiverError(E), } @@ -24,23 +25,36 @@ impl UdpTcServer { max_recv_size: usize, tc_receiver: Box>, ) -> Result { - Ok(Self { + let server = Self { socket: UdpSocket::bind(addr)?, recv_buf: vec![0; max_recv_size], sender_addr: None, tc_receiver, - }) + }; + server + .socket + .set_nonblocking(true) + .expect("Setting server non blocking failed"); + Ok(server) } - pub fn recv_tc(&mut self) -> Result<(usize, SocketAddr), ReceiveResult> { - let res = self - .socket - .recv_from(&mut self.recv_buf) - .map_err(|e| IoError(e))?; - self.sender_addr = Some(res.1); + pub fn try_recv_tc(&mut self) -> Result<(usize, SocketAddr), ReceiveResult> { + // .map_err(|e| IoError(e))?; + let res = match self.socket.recv_from(&mut self.recv_buf) { + Ok(res) => res, + Err(e) => { + if e.kind() != ErrorKind::WouldBlock { + return Err(ReceiveResult::WouldBlock); + } else { + return Err(ReceiveResult::OtherIoError(e)); + } + } + }; + let (num_bytes, from) = res; + self.sender_addr = Some(from); self.tc_receiver - .pass_tc(&self.recv_buf[0..res.0]) - .map_err(|e| ReceiverError(e))?; + .pass_tc(&self.recv_buf[0..num_bytes]) + .map_err(|e| ReceiveResult::ReceiverError(e))?; Ok(res) } } diff --git a/fsrc-example/src/bin/obsw.rs b/fsrc-example/src/bin/obsw.rs index f4ae995..6228fd2 100644 --- a/fsrc-example/src/bin/obsw.rs +++ b/fsrc-example/src/bin/obsw.rs @@ -1,17 +1,20 @@ use fsrc_core::hal::host::udp_server::{ReceiveResult, UdpTcServer}; +use fsrc_core::pool::{LocalPool, PoolCfg, StoreAddr}; use fsrc_core::tmtc::{ CcsdsDistributor, CcsdsError, CcsdsPacketHandler, PusDistributor, PusServiceProvider, ReceivesCcsdsTc, }; use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT}; use spacepackets::tc::PusTc; +use spacepackets::time::{CdsShortTimeProvider, TimeWriter}; +use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; use spacepackets::{CcsdsPacket, SpHeader}; use std::net::{IpAddr, SocketAddr}; +use std::sync::{mpsc, Arc, Mutex}; +use std::thread; const PUS_APID: u16 = 0x02; -struct PusReceiver {} - struct CcsdsReceiver { pus_handler: PusDistributor<()>, } @@ -46,6 +49,14 @@ impl CcsdsPacketHandler for CcsdsReceiver { } } +unsafe impl Send for CcsdsReceiver {} + +struct PusReceiver { + tm_apid: u16, + tm_tx: mpsc::Sender, + tm_store: Arc>, +} + impl PusServiceProvider for PusReceiver { type Error = (); @@ -59,38 +70,120 @@ impl PusServiceProvider for PusReceiver { println!("Received PUS ping command"); let raw_data = pus_tc.raw().expect("Could not retrieve raw data"); println!("Raw data: 0x{raw_data:x?}"); + let mut reply_header = SpHeader::tm(self.tm_apid, 0, 0).unwrap(); + let time_stamp = CdsShortTimeProvider::from_now().unwrap(); + let mut timestamp_buf = [0; 10]; + time_stamp.write_to_bytes(&mut timestamp_buf).unwrap(); + let tc_header = PusTmSecondaryHeader::new_simple(17, 2, ×tamp_buf); + let ping_reply = PusTm::new(&mut reply_header, tc_header, None, true); + let addr = self + .tm_store + .lock() + .expect("Locking TM store failed") + .add_pus_tm(&ping_reply); + self.tm_tx + .send(addr) + .expect("Sending TM to TM funnel failed"); } Ok(()) } } -fn main() { - let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); - let pus_receiver = PusReceiver {}; - let pus_distributor = PusDistributor::new(Box::new(pus_receiver)); - let ccsds_receiver = CcsdsReceiver { - pus_handler: pus_distributor, - }; - let ccsds_distributor = CcsdsDistributor::new(Box::new(ccsds_receiver)); - let mut udp_tmtc_server = UdpTcServer::new(addr, 2048, Box::new(ccsds_distributor)) - .expect("Creating UDP TMTC server failed"); - loop { - let res = udp_tmtc_server.recv_tc(); - match res { - Ok(_) => (), - Err(e) => match e { - ReceiveResult::ReceiverError(e) => match e { - CcsdsError::PacketError(e) => { - println!("Got packet error: {e:?}"); - } - CcsdsError::CustomError(_) => { - println!("Unknown receiver error") - } - }, - ReceiveResult::IoError(e) => { - println!("IO error {e}"); - } - }, - } +struct TmStore { + pool: LocalPool, +} + +impl TmStore { + fn add_pus_tm(&mut self, pus_tm: &PusTm) -> StoreAddr { + let (addr, mut buf) = self + .pool + .free_element(pus_tm.len_packed()) + .expect("Store error"); + pus_tm + .write_to(&mut buf) + .expect("Writing PUS TM to store failed"); + addr } } + +struct TmFunnel { + tm_funnel_rx: mpsc::Receiver, + tm_server_tx: mpsc::Sender, +} + +struct UdpTmtcServer { + udp_tc_server: UdpTcServer>, + tm_rx: mpsc::Receiver, + tm_store: Arc>, +} + +unsafe impl Send for UdpTmtcServer {} + +fn main() { + let pool_cfg = PoolCfg::new(vec![(8, 32), (4, 64), (2, 128)]); + let tm_pool = LocalPool::new(pool_cfg); + let tm_store = Arc::new(Mutex::new(TmStore { pool: tm_pool })); + let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); + let (tm_creator_tx, tm_funnel_rx) = mpsc::channel(); + let (tm_server_tx, tm_server_rx) = mpsc::channel(); + + let jh0 = thread::spawn(move || { + let pus_receiver = PusReceiver { + tm_apid: PUS_APID, + tm_tx: tm_creator_tx.clone(), + tm_store: tm_store.clone(), + }; + let pus_distributor = PusDistributor::new(Box::new(pus_receiver)); + let ccsds_receiver = CcsdsReceiver { + pus_handler: pus_distributor, + }; + let ccsds_distributor = CcsdsDistributor::new(Box::new(ccsds_receiver)); + let udp_tc_server = UdpTcServer::new(addr, 2048, Box::new(ccsds_distributor)) + .expect("Creating UDP TMTC server failed"); + let mut udp_tmtc_server = UdpTmtcServer { + udp_tc_server, + tm_rx: tm_server_rx, + tm_store: tm_store.clone(), + }; + loop { + let res = udp_tmtc_server.udp_tc_server.try_recv_tc(); + match res { + Ok(_) => (), + Err(e) => match e { + ReceiveResult::ReceiverError(e) => match e { + CcsdsError::PacketError(e) => { + println!("Got packet error: {e:?}"); + } + CcsdsError::CustomError(_) => { + println!("Unknown receiver error") + } + }, + ReceiveResult::OtherIoError(e) => { + println!("IO error {e}"); + } + ReceiveResult::WouldBlock => { + // TODO: Send TM Here + } + }, + } + } + }); + let jh1 = thread::spawn(move || { + let tm_funnel = TmFunnel { + tm_server_tx, + tm_funnel_rx, + }; + loop { + let res = tm_funnel.tm_funnel_rx.recv(); + if res.is_ok() { + let addr = res.unwrap(); + tm_funnel + .tm_server_tx + .send(addr) + .expect("Sending TM to server failed"); + } + } + }); + jh0.join().expect("Joining UDP TMTC server thread failed"); + jh1.join().expect("Joining TM Funnel thread failed"); +} From cc21785687ac021abfd34dbe73bb32259d09e9d2 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 29 Aug 2022 00:44:34 +0200 Subject: [PATCH 04/43] basic TMTC interaction established --- fsrc-core/src/hal/host/udp_server.rs | 6 +-- fsrc-example/src/bin/client.rs | 9 ++++ fsrc-example/src/bin/obsw.rs | 74 +++++++++++++++------------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/fsrc-core/src/hal/host/udp_server.rs b/fsrc-core/src/hal/host/udp_server.rs index bca2a1a..1414901 100644 --- a/fsrc-core/src/hal/host/udp_server.rs +++ b/fsrc-core/src/hal/host/udp_server.rs @@ -42,10 +42,10 @@ impl UdpTcServer { let res = match self.socket.recv_from(&mut self.recv_buf) { Ok(res) => res, Err(e) => { - if e.kind() != ErrorKind::WouldBlock { - return Err(ReceiveResult::WouldBlock); + return if e.kind() == ErrorKind::WouldBlock { + Err(ReceiveResult::WouldBlock) } else { - return Err(ReceiveResult::OtherIoError(e)); + Err(ReceiveResult::OtherIoError(e)) } } }; diff --git a/fsrc-example/src/bin/client.rs b/fsrc-example/src/bin/client.rs index 1a71a73..52b1034 100644 --- a/fsrc-example/src/bin/client.rs +++ b/fsrc-example/src/bin/client.rs @@ -2,6 +2,7 @@ use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT}; use spacepackets::tc::PusTc; use spacepackets::SpHeader; use std::net::{IpAddr, SocketAddr, UdpSocket}; +use std::time::Duration; fn main() { let mut buf = [0; 32]; @@ -13,4 +14,12 @@ fn main() { client .send_to(&buf[0..size], &addr) .expect(&*format!("Sending to {:?} failed", addr)); + client + .set_read_timeout(Some(Duration::from_secs(2))) + .expect("Setting read timeout failed"); + if let Ok(len) = client.recv(&mut buf) { + println!("Received TM with {} bytes", len); + } else { + println!("No reply received for 2 seconds or timeout"); + } } diff --git a/fsrc-example/src/bin/obsw.rs b/fsrc-example/src/bin/obsw.rs index ff09823..4d607fd 100644 --- a/fsrc-example/src/bin/obsw.rs +++ b/fsrc-example/src/bin/obsw.rs @@ -11,8 +11,8 @@ use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; use spacepackets::{CcsdsPacket, SpHeader}; use std::net::{IpAddr, SocketAddr}; use std::sync::{mpsc, Arc, Mutex}; -use std::sync::mpsc::TryRecvError; use std::thread; +use std::time::Duration; const PUS_APID: u16 = 0x02; @@ -96,12 +96,12 @@ struct TmStore { impl TmStore { fn add_pus_tm(&mut self, pus_tm: &PusTm) -> StoreAddr { - let (addr, mut buf) = self + let (addr, buf) = self .pool .free_element(pus_tm.len_packed()) .expect("Store error"); pus_tm - .write_to(&mut buf) + .write_to(buf) .expect("Writing PUS TM to store failed"); addr } @@ -144,39 +144,49 @@ fn main() { let mut udp_tmtc_server = UdpTmtcServer { udp_tc_server, tm_rx: tm_server_rx, - tm_store: tm_store.clone(), + tm_store }; loop { - let res = udp_tmtc_server.udp_tc_server.try_recv_tc(); - match res { - Ok(_) => (), - Err(e) => match e { - ReceiveResult::ReceiverError(e) => match e { - CcsdsError::PacketError(e) => { - println!("Got packet error: {e:?}"); + loop { + match udp_tmtc_server.udp_tc_server.try_recv_tc() { + Ok(_) => (), + Err(e) => match e { + ReceiveResult::ReceiverError(e) => match e { + CcsdsError::PacketError(e) => { + println!("Got packet error: {e:?}"); + } + CcsdsError::CustomError(_) => { + println!("Unknown receiver error") + } + }, + ReceiveResult::OtherIoError(e) => { + println!("IO error {e}"); + break; } - CcsdsError::CustomError(_) => { - println!("Unknown receiver error") + ReceiveResult::WouldBlock => { + if let Some(recv_addr) = udp_tmtc_server.udp_tc_server.last_sender() { + // TODO: Send TM Here + while let Ok(addr) = udp_tmtc_server.tm_rx.try_recv() { + let mut store_lock = udp_tmtc_server + .tm_store + .lock() + .expect("Locking TM store failed"); + let pg = store_lock.pool.read_with_guard(addr); + let buf = pg.read().expect("Error reading TM pool data"); + println!("Sending TM"); + udp_tmtc_server + .udp_tc_server + .socket + .send_to(buf, recv_addr) + .expect("Sending TM failed"); + } + } + break; } }, - ReceiveResult::OtherIoError(e) => { - println!("IO error {e}"); - } - ReceiveResult::WouldBlock => { - if let Some(recv_addr) = udp_tmtc_server.udp_tc_server.last_sender() { - // TODO: Send TM Here - match udp_tmtc_server.tm_rx.try_recv() { - Ok(addr) => { - udp_tmtc_server.tm_store.lock().expect("Locking TM store failed").pool.read() - udp_tmtc_server.udp_tc_server.socket.send_to() - } - Err(_) => {} - } - } - - } - }, + } } + thread::sleep(Duration::from_millis(400)); } }); let jh1 = thread::spawn(move || { @@ -185,9 +195,7 @@ fn main() { tm_funnel_rx, }; loop { - let res = tm_funnel.tm_funnel_rx.recv(); - if res.is_ok() { - let addr = res.unwrap(); + if let Ok(addr) = tm_funnel.tm_funnel_rx.recv() { tm_funnel .tm_server_tx .send(addr) From 56c8fa3dfcb114a6c2796e319f05b8c5fb3155ce Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 29 Aug 2022 01:33:32 +0200 Subject: [PATCH 05/43] obsw is multi-file bin now --- fsrc-core/src/tmtc/mod.rs | 1 + fsrc-core/src/tmtc/tm_helper.rs | 51 ++++++++ fsrc-example/src/bin/obsw.rs | 208 ------------------------------ fsrc-example/src/bin/obsw/main.rs | 54 ++++++++ fsrc-example/src/bin/obsw/pus.rs | 1 + fsrc-example/src/bin/obsw/tmtc.rs | 193 +++++++++++++++++++++++++++ 6 files changed, 300 insertions(+), 208 deletions(-) create mode 100644 fsrc-core/src/tmtc/tm_helper.rs delete mode 100644 fsrc-example/src/bin/obsw.rs create mode 100644 fsrc-example/src/bin/obsw/main.rs create mode 100644 fsrc-example/src/bin/obsw/pus.rs create mode 100644 fsrc-example/src/bin/obsw/tmtc.rs diff --git a/fsrc-core/src/tmtc/mod.rs b/fsrc-core/src/tmtc/mod.rs index d1d39b5..d9e954a 100644 --- a/fsrc-core/src/tmtc/mod.rs +++ b/fsrc-core/src/tmtc/mod.rs @@ -13,6 +13,7 @@ use spacepackets::SpHeader; pub mod ccsds_distrib; #[cfg(feature = "alloc")] pub mod pus_distrib; +pub mod tm_helper; pub use ccsds_distrib::{CcsdsDistributor, CcsdsError, CcsdsPacketHandler}; pub use pus_distrib::{PusDistributor, PusServiceProvider}; diff --git a/fsrc-core/src/tmtc/tm_helper.rs b/fsrc-core/src/tmtc/tm_helper.rs new file mode 100644 index 0000000..c8581bf --- /dev/null +++ b/fsrc-core/src/tmtc/tm_helper.rs @@ -0,0 +1,51 @@ +use spacepackets::time::{CdsShortTimeProvider, TimeWriter}; +use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; +use spacepackets::SpHeader; + +pub struct PusTmWithCdsShortHelper { + apid: u16, + cds_short_buf: [u8; 7], +} + +impl PusTmWithCdsShortHelper { + pub fn new(apid: u16) -> Self { + Self { + apid, + cds_short_buf: [0; 7], + } + } + + #[cfg(feature = "std")] + pub fn create_pus_tm_timestamp_now<'a>( + &'a mut self, + service: u8, + subservice: u8, + source_data: Option<&'a [u8]>, + ) -> PusTm { + let time_stamp = CdsShortTimeProvider::from_now().unwrap(); + time_stamp.write_to_bytes(&mut self.cds_short_buf).unwrap(); + self.create_pus_tm_common(service, subservice, source_data) + } + + pub fn create_pus_tm_with_stamp<'a>( + &'a mut self, + service: u8, + subservice: u8, + source_data: Option<&'a [u8]>, + stamper: &CdsShortTimeProvider, + ) -> PusTm { + stamper.write_to_bytes(&mut self.cds_short_buf).unwrap(); + self.create_pus_tm_common(service, subservice, source_data) + } + + fn create_pus_tm_common<'a>( + &'a self, + service: u8, + subservice: u8, + source_data: Option<&'a [u8]>, + ) -> PusTm { + let mut reply_header = SpHeader::tm(self.apid, 0, 0).unwrap(); + let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf); + PusTm::new(&mut reply_header, tc_header, source_data, true) + } +} diff --git a/fsrc-example/src/bin/obsw.rs b/fsrc-example/src/bin/obsw.rs deleted file mode 100644 index 4d607fd..0000000 --- a/fsrc-example/src/bin/obsw.rs +++ /dev/null @@ -1,208 +0,0 @@ -use fsrc_core::hal::host::udp_server::{ReceiveResult, UdpTcServer}; -use fsrc_core::pool::{LocalPool, PoolCfg, StoreAddr}; -use fsrc_core::tmtc::{ - CcsdsDistributor, CcsdsError, CcsdsPacketHandler, PusDistributor, PusServiceProvider, - ReceivesCcsdsTc, -}; -use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT}; -use spacepackets::tc::PusTc; -use spacepackets::time::{CdsShortTimeProvider, TimeWriter}; -use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; -use spacepackets::{CcsdsPacket, SpHeader}; -use std::net::{IpAddr, SocketAddr}; -use std::sync::{mpsc, Arc, Mutex}; -use std::thread; -use std::time::Duration; - -const PUS_APID: u16 = 0x02; - -struct CcsdsReceiver { - pus_handler: PusDistributor<()>, -} - -impl CcsdsPacketHandler for CcsdsReceiver { - type Error = (); - - fn valid_apids(&self) -> &'static [u16] { - &[PUS_APID] - } - - fn handle_known_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error> { - if sp_header.apid() == PUS_APID { - self.pus_handler - .pass_ccsds(sp_header, tc_raw) - .expect("Handling PUS packet failed"); - } - Ok(()) - } - - fn handle_unknown_apid( - &mut self, - _sp_header: &SpHeader, - _tc_raw: &[u8], - ) -> Result<(), Self::Error> { - println!("Unknown APID detected"); - Ok(()) - } -} - -unsafe impl Send for CcsdsReceiver {} - -struct PusReceiver { - tm_apid: u16, - tm_tx: mpsc::Sender, - tm_store: Arc>, -} - -impl PusServiceProvider for PusReceiver { - type Error = (); - - fn handle_pus_tc_packet( - &mut self, - service: u8, - _header: &SpHeader, - pus_tc: &PusTc, - ) -> Result<(), Self::Error> { - if service == 17 { - println!("Received PUS ping command"); - let raw_data = pus_tc.raw().expect("Could not retrieve raw data"); - println!("Raw data: 0x{raw_data:x?}"); - let mut reply_header = SpHeader::tm(self.tm_apid, 0, 0).unwrap(); - let time_stamp = CdsShortTimeProvider::from_now().unwrap(); - let mut timestamp_buf = [0; 10]; - time_stamp.write_to_bytes(&mut timestamp_buf).unwrap(); - let tc_header = PusTmSecondaryHeader::new_simple(17, 2, ×tamp_buf); - let ping_reply = PusTm::new(&mut reply_header, tc_header, None, true); - let addr = self - .tm_store - .lock() - .expect("Locking TM store failed") - .add_pus_tm(&ping_reply); - self.tm_tx - .send(addr) - .expect("Sending TM to TM funnel failed"); - } - Ok(()) - } -} - -struct TmStore { - pool: LocalPool, -} - -impl TmStore { - fn add_pus_tm(&mut self, pus_tm: &PusTm) -> StoreAddr { - let (addr, buf) = self - .pool - .free_element(pus_tm.len_packed()) - .expect("Store error"); - pus_tm - .write_to(buf) - .expect("Writing PUS TM to store failed"); - addr - } -} - -struct TmFunnel { - tm_funnel_rx: mpsc::Receiver, - tm_server_tx: mpsc::Sender, -} - -struct UdpTmtcServer { - udp_tc_server: UdpTcServer>, - tm_rx: mpsc::Receiver, - tm_store: Arc>, -} - -unsafe impl Send for UdpTmtcServer {} - -fn main() { - let pool_cfg = PoolCfg::new(vec![(8, 32), (4, 64), (2, 128)]); - let tm_pool = LocalPool::new(pool_cfg); - let tm_store = Arc::new(Mutex::new(TmStore { pool: tm_pool })); - let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); - let (tm_creator_tx, tm_funnel_rx) = mpsc::channel(); - let (tm_server_tx, tm_server_rx) = mpsc::channel(); - - let jh0 = thread::spawn(move || { - let pus_receiver = PusReceiver { - tm_apid: PUS_APID, - tm_tx: tm_creator_tx.clone(), - tm_store: tm_store.clone(), - }; - let pus_distributor = PusDistributor::new(Box::new(pus_receiver)); - let ccsds_receiver = CcsdsReceiver { - pus_handler: pus_distributor, - }; - let ccsds_distributor = CcsdsDistributor::new(Box::new(ccsds_receiver)); - let udp_tc_server = UdpTcServer::new(addr, 2048, Box::new(ccsds_distributor)) - .expect("Creating UDP TMTC server failed"); - let mut udp_tmtc_server = UdpTmtcServer { - udp_tc_server, - tm_rx: tm_server_rx, - tm_store - }; - loop { - loop { - match udp_tmtc_server.udp_tc_server.try_recv_tc() { - Ok(_) => (), - Err(e) => match e { - ReceiveResult::ReceiverError(e) => match e { - CcsdsError::PacketError(e) => { - println!("Got packet error: {e:?}"); - } - CcsdsError::CustomError(_) => { - println!("Unknown receiver error") - } - }, - ReceiveResult::OtherIoError(e) => { - println!("IO error {e}"); - break; - } - ReceiveResult::WouldBlock => { - if let Some(recv_addr) = udp_tmtc_server.udp_tc_server.last_sender() { - // TODO: Send TM Here - while let Ok(addr) = udp_tmtc_server.tm_rx.try_recv() { - let mut store_lock = udp_tmtc_server - .tm_store - .lock() - .expect("Locking TM store failed"); - let pg = store_lock.pool.read_with_guard(addr); - let buf = pg.read().expect("Error reading TM pool data"); - println!("Sending TM"); - udp_tmtc_server - .udp_tc_server - .socket - .send_to(buf, recv_addr) - .expect("Sending TM failed"); - } - } - break; - } - }, - } - } - thread::sleep(Duration::from_millis(400)); - } - }); - let jh1 = thread::spawn(move || { - let tm_funnel = TmFunnel { - tm_server_tx, - tm_funnel_rx, - }; - loop { - if let Ok(addr) = tm_funnel.tm_funnel_rx.recv() { - tm_funnel - .tm_server_tx - .send(addr) - .expect("Sending TM to server failed"); - } - } - }); - jh0.join().expect("Joining UDP TMTC server thread failed"); - jh1.join().expect("Joining TM Funnel thread failed"); -} diff --git a/fsrc-example/src/bin/obsw/main.rs b/fsrc-example/src/bin/obsw/main.rs new file mode 100644 index 0000000..89e9f2e --- /dev/null +++ b/fsrc-example/src/bin/obsw/main.rs @@ -0,0 +1,54 @@ +mod pus; +mod tmtc; + +use crate::tmtc::{core_tmtc_task, TmStore}; +use fsrc_core::hal::host::udp_server::UdpTcServer; +use fsrc_core::pool::{LocalPool, PoolCfg, StoreAddr}; +use fsrc_core::tmtc::CcsdsError; +use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT}; +use std::net::{IpAddr, SocketAddr}; +use std::sync::{mpsc, Arc, Mutex}; +use std::thread; + +struct TmFunnel { + tm_funnel_rx: mpsc::Receiver, + tm_server_tx: mpsc::Sender, +} + +struct UdpTmtcServer { + udp_tc_server: UdpTcServer>, + tm_rx: mpsc::Receiver, + tm_store: Arc>, +} + +unsafe impl Send for UdpTmtcServer {} + +fn main() { + let pool_cfg = PoolCfg::new(vec![(8, 32), (4, 64), (2, 128)]); + let tm_pool = LocalPool::new(pool_cfg); + let tm_store = Arc::new(Mutex::new(TmStore { pool: tm_pool })); + let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); + let (tm_creator_tx, tm_funnel_rx) = mpsc::channel(); + let (tm_server_tx, tm_server_rx) = mpsc::channel(); + + let jh0 = thread::spawn(move || { + core_tmtc_task(tm_creator_tx.clone(), tm_server_rx, tm_store.clone(), addr); + }); + + let jh1 = thread::spawn(move || { + let tm_funnel = TmFunnel { + tm_server_tx, + tm_funnel_rx, + }; + loop { + if let Ok(addr) = tm_funnel.tm_funnel_rx.recv() { + tm_funnel + .tm_server_tx + .send(addr) + .expect("Sending TM to server failed"); + } + } + }); + jh0.join().expect("Joining UDP TMTC server thread failed"); + jh1.join().expect("Joining TM Funnel thread failed"); +} diff --git a/fsrc-example/src/bin/obsw/pus.rs b/fsrc-example/src/bin/obsw/pus.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fsrc-example/src/bin/obsw/pus.rs @@ -0,0 +1 @@ + diff --git a/fsrc-example/src/bin/obsw/tmtc.rs b/fsrc-example/src/bin/obsw/tmtc.rs new file mode 100644 index 0000000..b28cdb4 --- /dev/null +++ b/fsrc-example/src/bin/obsw/tmtc.rs @@ -0,0 +1,193 @@ +use fsrc_core::hal::host::udp_server::{ReceiveResult, UdpTcServer}; +use std::net::SocketAddr; +use std::sync::{mpsc, Arc, Mutex}; +use std::thread; +use std::time::Duration; + +use crate::UdpTmtcServer; +use fsrc_core::pool::{LocalPool, StoreAddr}; +use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper; +use fsrc_core::tmtc::{ + CcsdsDistributor, CcsdsError, CcsdsPacketHandler, PusDistributor, PusServiceProvider, + ReceivesCcsdsTc, +}; +use spacepackets::tc::PusTc; +use spacepackets::tm::PusTm; +use spacepackets::{CcsdsPacket, SpHeader}; + +pub const PUS_APID: u16 = 0x02; + +pub struct TmStore { + pub pool: LocalPool, +} + +impl TmStore { + fn add_pus_tm(&mut self, pus_tm: &PusTm) -> StoreAddr { + let (addr, buf) = self + .pool + .free_element(pus_tm.len_packed()) + .expect("Store error"); + pus_tm + .write_to(buf) + .expect("Writing PUS TM to store failed"); + addr + } +} + +pub struct CcsdsReceiver { + pub pus_handler: PusDistributor<()>, +} + +impl CcsdsPacketHandler for CcsdsReceiver { + type Error = (); + + fn valid_apids(&self) -> &'static [u16] { + &[PUS_APID] + } + + fn handle_known_apid( + &mut self, + sp_header: &SpHeader, + tc_raw: &[u8], + ) -> Result<(), Self::Error> { + if sp_header.apid() == PUS_APID { + self.pus_handler + .pass_ccsds(sp_header, tc_raw) + .expect("Handling PUS packet failed"); + } + Ok(()) + } + + fn handle_unknown_apid( + &mut self, + _sp_header: &SpHeader, + _tc_raw: &[u8], + ) -> Result<(), Self::Error> { + println!("Unknown APID detected"); + Ok(()) + } +} + +unsafe impl Send for CcsdsReceiver {} + +pub struct PusReceiver { + pub tm_helper: PusTmWithCdsShortHelper, + pub tm_tx: mpsc::Sender, + pub tm_store: Arc>, +} + +impl PusReceiver { + pub fn new(apid: u16, tm_tx: mpsc::Sender, tm_store: Arc>) -> Self { + Self { + tm_helper: PusTmWithCdsShortHelper::new(apid), + tm_tx, + tm_store, + } + } +} + +impl PusServiceProvider for PusReceiver { + type Error = (); + + fn handle_pus_tc_packet( + &mut self, + service: u8, + _header: &SpHeader, + pus_tc: &PusTc, + ) -> Result<(), Self::Error> { + if service == 17 { + self.handle_test_service(pus_tc); + } + Ok(()) + } +} + +impl PusReceiver { + fn handle_test_service(&mut self, pus_tc: &PusTc) { + println!("Received PUS ping command"); + let raw_data = pus_tc.raw().expect("Could not retrieve raw data"); + println!("Raw data: 0x{raw_data:x?}"); + println!("Sending ping reply"); + let ping_reply = self.tm_helper.create_pus_tm_timestamp_now(17, 2, None); + let addr = self + .tm_store + .lock() + .expect("Locking TM store failed") + .add_pus_tm(&ping_reply); + self.tm_tx + .send(addr) + .expect("Sending TM to TM funnel failed"); + } +} + +pub fn core_tmtc_task( + tm_creator_tx: mpsc::Sender, + tm_server_rx: mpsc::Receiver, + tm_store: Arc>, + addr: SocketAddr, +) { + let pus_receiver = PusReceiver::new(PUS_APID, tm_creator_tx, tm_store.clone()); + let pus_distributor = PusDistributor::new(Box::new(pus_receiver)); + let ccsds_receiver = CcsdsReceiver { + pus_handler: pus_distributor, + }; + let ccsds_distributor = CcsdsDistributor::new(Box::new(ccsds_receiver)); + let udp_tc_server = UdpTcServer::new(addr, 2048, Box::new(ccsds_distributor)) + .expect("Creating UDP TMTC server failed"); + let mut udp_tmtc_server = UdpTmtcServer { + udp_tc_server, + tm_rx: tm_server_rx, + tm_store, + }; + loop { + core_tmtc_loop(&mut udp_tmtc_server); + thread::sleep(Duration::from_millis(400)); + } +} + +fn core_tmtc_loop(udp_tmtc_server: &mut UdpTmtcServer) { + while core_tc_handling(udp_tmtc_server) {} + if let Some(recv_addr) = udp_tmtc_server.udp_tc_server.last_sender() { + core_tm_handling(udp_tmtc_server, &recv_addr); + } +} + +fn core_tc_handling(udp_tmtc_server: &mut UdpTmtcServer) -> bool { + match udp_tmtc_server.udp_tc_server.try_recv_tc() { + Ok(_) => true, + Err(e) => match e { + ReceiveResult::ReceiverError(e) => match e { + CcsdsError::PacketError(e) => { + println!("Got packet error: {e:?}"); + true + } + CcsdsError::CustomError(_) => { + println!("Unknown receiver error"); + true + } + }, + ReceiveResult::OtherIoError(e) => { + println!("IO error {e}"); + false + } + ReceiveResult::WouldBlock => false, + }, + } +} + +fn core_tm_handling(udp_tmtc_server: &mut UdpTmtcServer, recv_addr: &SocketAddr) { + while let Ok(addr) = udp_tmtc_server.tm_rx.try_recv() { + let mut store_lock = udp_tmtc_server + .tm_store + .lock() + .expect("Locking TM store failed"); + let pg = store_lock.pool.read_with_guard(addr); + let buf = pg.read().expect("Error reading TM pool data"); + println!("Sending TM"); + udp_tmtc_server + .udp_tc_server + .socket + .send_to(buf, recv_addr) + .expect("Sending TM failed"); + } +} From d3469f594d494947f2b1b6bf18e7b1a1aa6a71a9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 3 Sep 2022 13:47:25 +0200 Subject: [PATCH 06/43] better printout format --- Cargo.lock | 232 +++++++++++++++++------------ fsrc-example/src/bin/client.rs | 17 ++- fsrc-example/src/bin/obsw/ccsds.rs | 37 +++++ fsrc-example/src/bin/obsw/main.rs | 2 + fsrc-example/src/bin/obsw/pus.rs | 56 +++++++ fsrc-example/src/bin/obsw/tmtc.rs | 98 +----------- 6 files changed, 252 insertions(+), 190 deletions(-) create mode 100644 fsrc-example/src/bin/obsw/ccsds.rs diff --git a/Cargo.lock b/Cargo.lock index db6f485..5c38b37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,16 +23,19 @@ dependencies = [ ] [[package]] -name = "atomic-option" -version = "0.1.2" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0db678acb667b525ac40a324fc5f7d3390e29239b31c7327bb8157f5b4fff593" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] [[package]] name = "atomic-polyfill" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14bf7b4f565e5e717d7a7a65b2a05c0b8c96e4db636d6f780f03b15108cdd1b" +checksum = "9c041a8d9751a520ee19656232a18971f18946a7900f1520ee4400002244dd89" dependencies = [ "critical-section", ] @@ -78,17 +81,16 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "bus" -version = "2.2.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e66e1779f5b1440f1a58220ba3b3ded4427175f0a9fb8d7066521f8b4e8f2b" +checksum = "80cb4625f5b60155ff1018c9d4ce2e38bf5ae3e5780dfab9fa68bb44a6b751e2" dependencies = [ - "atomic-option", "crossbeam-channel", "num_cpus", "parking_lot_core", @@ -100,12 +102,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -114,26 +110,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6127248204b9aba09a362f6c930ef6a78f2c1b2215f8a7b398c06e1083f17af0" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ - "js-sys", + "iana-time-zone", "num-integer", "num-traits", - "wasm-bindgen", "winapi", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "cobs" version = "0.2.3" @@ -141,10 +127,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] -name = "cortex-m" -version = "0.7.5" +name = "core-foundation-sys" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd20d4ac4aa86f4f75f239d59e542ef67de87cce2c282818dc6e84155d3ea126" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cortex-m" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70858629a458fdfd39f9675c4dc309411f2a3f83bede76988d81bf1a0ecee9e0" dependencies = [ "bare-metal 0.2.5", "bitfield", @@ -174,30 +166,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95da181745b56d4bd339530ec393508910c909c784e8962d15d722bacf0bcbcd" dependencies = [ "bare-metal 1.0.0", - "cfg-if 1.0.0", + "cfg-if", "cortex-m", "riscv", ] [[package]] name = "crossbeam-channel" -version = "0.4.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ + "cfg-if", "crossbeam-utils", - "maybe-uninit", ] [[package]] name = "crossbeam-utils" -version = "0.7.2" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "lazy_static", + "cfg-if", + "once_cell", ] [[package]] @@ -258,7 +249,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] @@ -283,9 +274,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.14" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065681e99f9ef7e0e813702a0326aedbcbbde7db5e55f097aedd1bf50b9dca43" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" dependencies = [ "atomic-polyfill", "hash32", @@ -304,6 +295,20 @@ dependencies = [ "libc", ] +[[package]] +name = "iana-time-zone" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "js-sys", + "once_cell", + "wasm-bindgen", + "winapi", +] + [[package]] name = "js-sys" version = "0.3.59" @@ -321,15 +326,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" dependencies = [ "autocfg", "scopeguard", @@ -341,15 +346,9 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.5.0" @@ -402,29 +401,28 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "parking_lot_core" -version = "0.7.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "cfg-if", "libc", "redox_syscall", "smallvec", - "winapi", + "windows-sys", ] [[package]] name = "postcard" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f5465c5e5a38e04552d8fb53ebcf4f58124ab3bbd0c02add043b33f82792e5" +checksum = "1c2b180dc0bade59f03fd005cb967d3f1e5f69b13922dad0cd6e047cb8af2363" dependencies = [ "cobs", "heapless", @@ -433,33 +431,36 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] [[package]] name = "regex" -version = "1.5.6" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -468,9 +469,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "riscv" @@ -508,7 +509,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.10", + "semver 1.0.13", ] [[package]] @@ -528,9 +529,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" +checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" [[package]] name = "semver-parser" @@ -540,18 +541,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.143" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.143" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ "proc-macro2", "quote", @@ -560,9 +561,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "spacepackets" @@ -579,9 +580,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" dependencies = [ "lock_api", ] @@ -594,9 +595,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" -version = "1.0.96" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -617,18 +618,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09" dependencies = [ "proc-macro2", "quote", @@ -637,9 +638,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unicode-xid" @@ -686,7 +687,7 @@ version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -756,6 +757,49 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + [[package]] name = "zerocopy" version = "0.6.1" diff --git a/fsrc-example/src/bin/client.rs b/fsrc-example/src/bin/client.rs index 52b1034..8523e9e 100644 --- a/fsrc-example/src/bin/client.rs +++ b/fsrc-example/src/bin/client.rs @@ -1,11 +1,14 @@ use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT}; +use spacepackets::ecss::PusPacket; use spacepackets::tc::PusTc; +use spacepackets::tm::PusTm; use spacepackets::SpHeader; use std::net::{IpAddr, SocketAddr, UdpSocket}; use std::time::Duration; fn main() { let mut buf = [0; 32]; + println!("Packing and sending PUS ping command TC[17,1]"); let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); let mut sph = SpHeader::tc(0x02, 0, 0).unwrap(); let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); @@ -17,8 +20,18 @@ fn main() { client .set_read_timeout(Some(Duration::from_secs(2))) .expect("Setting read timeout failed"); - if let Ok(len) = client.recv(&mut buf) { - println!("Received TM with {} bytes", len); + if let Ok(_len) = client.recv(&mut buf) { + let (pus_tm, size) = PusTm::new_from_raw_slice(&buf, 7).expect("Parsing PUS TM failed"); + if pus_tm.service() == 17 && pus_tm.subservice() == 2 { + println!("Received PUS Ping Reply TM[17,2]") + } else { + println!( + "Received TM[{}, {}] with {} bytes", + pus_tm.service(), + pus_tm.subservice(), + size + ); + } } else { println!("No reply received for 2 seconds or timeout"); } diff --git a/fsrc-example/src/bin/obsw/ccsds.rs b/fsrc-example/src/bin/obsw/ccsds.rs new file mode 100644 index 0000000..b058e4d --- /dev/null +++ b/fsrc-example/src/bin/obsw/ccsds.rs @@ -0,0 +1,37 @@ +use crate::tmtc::PUS_APID; +use fsrc_core::tmtc::{CcsdsPacketHandler, PusDistributor, ReceivesCcsdsTc}; +use spacepackets::{CcsdsPacket, SpHeader}; + +pub struct CcsdsReceiver { + pub pus_handler: PusDistributor<()>, +} + +impl CcsdsPacketHandler for CcsdsReceiver { + type Error = (); + + fn valid_apids(&self) -> &'static [u16] { + &[PUS_APID] + } + + fn handle_known_apid( + &mut self, + sp_header: &SpHeader, + tc_raw: &[u8], + ) -> Result<(), Self::Error> { + if sp_header.apid() == PUS_APID { + self.pus_handler + .pass_ccsds(sp_header, tc_raw) + .expect("Handling PUS packet failed"); + } + Ok(()) + } + + fn handle_unknown_apid( + &mut self, + _sp_header: &SpHeader, + _tc_raw: &[u8], + ) -> Result<(), Self::Error> { + println!("Unknown APID detected"); + Ok(()) + } +} diff --git a/fsrc-example/src/bin/obsw/main.rs b/fsrc-example/src/bin/obsw/main.rs index 89e9f2e..728196c 100644 --- a/fsrc-example/src/bin/obsw/main.rs +++ b/fsrc-example/src/bin/obsw/main.rs @@ -1,3 +1,4 @@ +mod ccsds; mod pus; mod tmtc; @@ -24,6 +25,7 @@ struct UdpTmtcServer { unsafe impl Send for UdpTmtcServer {} fn main() { + println!("Running OBSW example"); let pool_cfg = PoolCfg::new(vec![(8, 32), (4, 64), (2, 128)]); let tm_pool = LocalPool::new(pool_cfg); let tm_store = Arc::new(Mutex::new(TmStore { pool: tm_pool })); diff --git a/fsrc-example/src/bin/obsw/pus.rs b/fsrc-example/src/bin/obsw/pus.rs index 8b13789..ee7e9cf 100644 --- a/fsrc-example/src/bin/obsw/pus.rs +++ b/fsrc-example/src/bin/obsw/pus.rs @@ -1 +1,57 @@ +use crate::TmStore; +use fsrc_core::pool::StoreAddr; +use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper; +use fsrc_core::tmtc::PusServiceProvider; +use spacepackets::tc::{PusTc, PusTcSecondaryHeaderT}; +use spacepackets::SpHeader; +use std::sync::{mpsc, Arc, Mutex}; +pub struct PusReceiver { + pub tm_helper: PusTmWithCdsShortHelper, + pub tm_tx: mpsc::Sender, + pub tm_store: Arc>, +} + +impl PusReceiver { + pub fn new(apid: u16, tm_tx: mpsc::Sender, tm_store: Arc>) -> Self { + Self { + tm_helper: PusTmWithCdsShortHelper::new(apid), + tm_tx, + tm_store, + } + } +} + +impl PusServiceProvider for PusReceiver { + type Error = (); + + fn handle_pus_tc_packet( + &mut self, + service: u8, + _header: &SpHeader, + pus_tc: &PusTc, + ) -> Result<(), Self::Error> { + if service == 17 { + self.handle_test_service(pus_tc); + } + Ok(()) + } +} + +impl PusReceiver { + fn handle_test_service(&mut self, pus_tc: &PusTc) { + if pus_tc.subservice() == 1 { + println!("Received PUS ping command TC[17,1]"); + println!("Sending ping reply PUS TM[17,2]"); + let ping_reply = self.tm_helper.create_pus_tm_timestamp_now(17, 2, None); + let addr = self + .tm_store + .lock() + .expect("Locking TM store failed") + .add_pus_tm(&ping_reply); + self.tm_tx + .send(addr) + .expect("Sending TM to TM funnel failed"); + } + } +} diff --git a/fsrc-example/src/bin/obsw/tmtc.rs b/fsrc-example/src/bin/obsw/tmtc.rs index b28cdb4..683569a 100644 --- a/fsrc-example/src/bin/obsw/tmtc.rs +++ b/fsrc-example/src/bin/obsw/tmtc.rs @@ -4,16 +4,12 @@ use std::sync::{mpsc, Arc, Mutex}; use std::thread; use std::time::Duration; +use crate::ccsds::CcsdsReceiver; +use crate::pus::PusReceiver; use crate::UdpTmtcServer; use fsrc_core::pool::{LocalPool, StoreAddr}; -use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper; -use fsrc_core::tmtc::{ - CcsdsDistributor, CcsdsError, CcsdsPacketHandler, PusDistributor, PusServiceProvider, - ReceivesCcsdsTc, -}; -use spacepackets::tc::PusTc; +use fsrc_core::tmtc::{CcsdsDistributor, CcsdsError, PusDistributor}; use spacepackets::tm::PusTm; -use spacepackets::{CcsdsPacket, SpHeader}; pub const PUS_APID: u16 = 0x02; @@ -22,7 +18,7 @@ pub struct TmStore { } impl TmStore { - fn add_pus_tm(&mut self, pus_tm: &PusTm) -> StoreAddr { + pub fn add_pus_tm(&mut self, pus_tm: &PusTm) -> StoreAddr { let (addr, buf) = self .pool .free_element(pus_tm.len_packed()) @@ -34,92 +30,6 @@ impl TmStore { } } -pub struct CcsdsReceiver { - pub pus_handler: PusDistributor<()>, -} - -impl CcsdsPacketHandler for CcsdsReceiver { - type Error = (); - - fn valid_apids(&self) -> &'static [u16] { - &[PUS_APID] - } - - fn handle_known_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error> { - if sp_header.apid() == PUS_APID { - self.pus_handler - .pass_ccsds(sp_header, tc_raw) - .expect("Handling PUS packet failed"); - } - Ok(()) - } - - fn handle_unknown_apid( - &mut self, - _sp_header: &SpHeader, - _tc_raw: &[u8], - ) -> Result<(), Self::Error> { - println!("Unknown APID detected"); - Ok(()) - } -} - -unsafe impl Send for CcsdsReceiver {} - -pub struct PusReceiver { - pub tm_helper: PusTmWithCdsShortHelper, - pub tm_tx: mpsc::Sender, - pub tm_store: Arc>, -} - -impl PusReceiver { - pub fn new(apid: u16, tm_tx: mpsc::Sender, tm_store: Arc>) -> Self { - Self { - tm_helper: PusTmWithCdsShortHelper::new(apid), - tm_tx, - tm_store, - } - } -} - -impl PusServiceProvider for PusReceiver { - type Error = (); - - fn handle_pus_tc_packet( - &mut self, - service: u8, - _header: &SpHeader, - pus_tc: &PusTc, - ) -> Result<(), Self::Error> { - if service == 17 { - self.handle_test_service(pus_tc); - } - Ok(()) - } -} - -impl PusReceiver { - fn handle_test_service(&mut self, pus_tc: &PusTc) { - println!("Received PUS ping command"); - let raw_data = pus_tc.raw().expect("Could not retrieve raw data"); - println!("Raw data: 0x{raw_data:x?}"); - println!("Sending ping reply"); - let ping_reply = self.tm_helper.create_pus_tm_timestamp_now(17, 2, None); - let addr = self - .tm_store - .lock() - .expect("Locking TM store failed") - .add_pus_tm(&ping_reply); - self.tm_tx - .send(addr) - .expect("Sending TM to TM funnel failed"); - } -} - pub fn core_tmtc_task( tm_creator_tx: mpsc::Sender, tm_server_rx: mpsc::Receiver, From 3c1ed36b11035f21d2fd32fcb3d8b2c8441ca29d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 3 Sep 2022 16:30:37 +0200 Subject: [PATCH 07/43] borrowing rules make this difficult --- fsrc-core/src/lib.rs | 1 + fsrc-core/src/pus/mod.rs | 11 +++ fsrc-core/src/pus/verification.rs | 140 ++++++++++++++++++++++++++++++ spacepackets | 2 +- 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 fsrc-core/src/pus/mod.rs create mode 100644 fsrc-core/src/pus/verification.rs diff --git a/fsrc-core/src/lib.rs b/fsrc-core/src/lib.rs index 70d4b93..02298a0 100644 --- a/fsrc-core/src/lib.rs +++ b/fsrc-core/src/lib.rs @@ -23,6 +23,7 @@ pub mod hal; pub mod objects; #[cfg(feature = "alloc")] pub mod pool; +pub mod pus; pub mod tmtc; extern crate downcast_rs; diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs new file mode 100644 index 0000000..20c53ec --- /dev/null +++ b/fsrc-core/src/pus/mod.rs @@ -0,0 +1,11 @@ +use crate::pool::StoreError; +use spacepackets::time::TimestampError; + +pub mod verification; + +#[derive(Debug, Copy, Clone)] +pub enum SendStoredTmError { + SendError(E), + TimeStampError(TimestampError), + StoreError(StoreError), +} diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs new file mode 100644 index 0000000..b750f27 --- /dev/null +++ b/fsrc-core/src/pus/verification.rs @@ -0,0 +1,140 @@ +use crate::pus::SendStoredTmError; +use alloc::boxed::Box; +use alloc::vec::Vec; +use spacepackets::tc::PusTc; +use spacepackets::time::{TimeWriter, TimestampError}; +use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; +use spacepackets::SpHeader; +use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; +use std::marker::PhantomData; + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub struct RequestId { + version_number: u8, + packet_id: PacketId, + psc: PacketSequenceCtrl, +} + +impl RequestId { + pub fn new(tc: &PusTc) -> Self { + RequestId { + version_number: tc.ccsds_version(), + packet_id: tc.packet_id(), + psc: tc.psc(), + } + } +} + +pub trait VerificationSender { + fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), SendStoredTmError>; +} + +pub struct VerificationReporter { + apid: u16, + msg_count: u16, + dest_id: u16, + time_stamper: Box, + time_stamp_buf: Vec, +} + +impl VerificationReporter { + pub fn add_tc(&mut self, req_id: RequestId) -> VerificationToken { + VerificationToken::::new(req_id) + } + + pub fn acceptance_success( + &mut self, + token: VerificationToken, + sender: &mut impl VerificationSender, + ) -> Result, SendStoredTmError> { + let tm = self + .create_tm(1, 1) + .map_err(|e| SendStoredTmError::TimeStampError(e))?; + sender.send_verification_tm(tm)?; + self.msg_count += 1; + Ok(VerificationToken { + state: PhantomData, + req_id: token.req_id, + }) + } + + pub fn acceptance_failure(self, _token: VerificationToken) { + unimplemented!(); + } + + pub fn start_success( + &mut self, + token: VerificationToken, + ) -> VerificationToken { + VerificationToken { + state: PhantomData, + req_id: token.req_id, + } + } + + pub fn start_failure(&mut self, _token: VerificationToken) { + unimplemented!(); + } + + pub fn step_success(&mut self, _token: &VerificationToken) { + unimplemented!(); + } + + pub fn step_failure(&mut self, _token: VerificationToken) { + unimplemented!(); + } + + pub fn completion_success(&mut self, _token: VerificationToken) { + unimplemented!(); + } + pub fn completion_failure(&mut self, _token: VerificationToken) { + unimplemented!(); + } + + fn create_tm(&mut self, service: u8, subservice: u8) -> Result { + let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap(); + // I think it is okay to panic here. This error should never happen, I consider + // this a configuration error. + self.time_stamper + .write_to_bytes(self.time_stamp_buf.as_mut_slice())?; + let tm_sec_header = PusTmSecondaryHeader::new( + service, + subservice, + self.msg_count, + self.dest_id, + &self.time_stamp_buf, + ); + Ok(PusTm::new(&mut sp_header, tm_sec_header, None, true)) + } +} + +pub struct VerificationToken { + state: PhantomData, + req_id: RequestId, +} + +pub struct StateNone; +pub struct StateAccepted; +pub struct StateStarted; + +impl VerificationToken { + fn new(req_id: RequestId) -> VerificationToken { + VerificationToken { + state: PhantomData, + req_id, + } + } +} + +#[cfg(test)] +mod tests { + use crate::pus::verification::VerificationToken; + + #[test] + pub fn test_basic_type_state() { + //let mut reporter = VerificationToken::new(); + //let mut accepted = reporter.acceptance_success(); + //let started = accepted.start_success(); + //started.completion_success(); + } +} diff --git a/spacepackets b/spacepackets index 42d3487..96d389a 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit 42d3487c1934cfecf9b2d599c2f81fde52a61cc3 +Subproject commit 96d389a6515f763f179c23742a9f578659ae9509 From a34497bf3ccdf34f3a2d09671f95127cce624795 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 3 Sep 2022 17:09:36 +0200 Subject: [PATCH 08/43] conitnued verif reporter --- fsrc-core/src/pus/mod.rs | 2 +- fsrc-core/src/pus/verification.rs | 94 ++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs index 20c53ec..c18a6cf 100644 --- a/fsrc-core/src/pus/mod.rs +++ b/fsrc-core/src/pus/mod.rs @@ -3,7 +3,7 @@ use spacepackets::time::TimestampError; pub mod verification; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub enum SendStoredTmError { SendError(E), TimeStampError(TimestampError), diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index b750f27..86a6542 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -1,8 +1,10 @@ use crate::pus::SendStoredTmError; use alloc::boxed::Box; +use alloc::vec; use alloc::vec::Vec; +use core::mem::size_of; use spacepackets::tc::PusTc; -use spacepackets::time::{TimeWriter, TimestampError}; +use spacepackets::time::{CcsdsTimeProvider, TimeWriter, TimestampError}; use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; use spacepackets::SpHeader; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; @@ -15,6 +17,16 @@ pub struct RequestId { psc: PacketSequenceCtrl, } +impl RequestId { + const SIZE_AS_BYTES: usize = size_of::(); + + pub fn to_bytes(&self, buf: &mut [u8]) { + let raw = ((self.version_number as u32) << 31) + | ((self.packet_id.raw() as u32) << 16) + | self.psc.raw() as u32; + buf.copy_from_slice(raw.to_be_bytes().as_slice()); + } +} impl RequestId { pub fn new(tc: &PusTc) -> Self { RequestId { @@ -30,14 +42,57 @@ pub trait VerificationSender { } pub struct VerificationReporter { - apid: u16, + pub apid: u16, + pub dest_id: u16, msg_count: u16, - dest_id: u16, time_stamper: Box, time_stamp_buf: Vec, + source_data_buf: Vec, +} + +pub struct VerificationReporterCfg { + pub apid: u16, + pub dest_id: u16, + pub time_stamper: Box, + pub step_field_width: u8, + pub failure_code_field_width: u8, + pub max_fail_data_len: usize, + pub max_stamp_len: usize, +} + +impl VerificationReporterCfg { + pub fn new(time_stamper: impl TimeWriter + CcsdsTimeProvider + 'static, apid: u16) -> Self { + let max_stamp_len = time_stamper.len_as_bytes(); + Self { + apid, + dest_id: 0, + time_stamper: Box::new(time_stamper), + step_field_width: size_of::() as u8, + failure_code_field_width: size_of::() as u8, + max_fail_data_len: 2 * size_of::(), + max_stamp_len, + } + } } impl VerificationReporter { + pub fn new(cfg: VerificationReporterCfg) -> Self { + Self { + apid: cfg.apid, + dest_id: cfg.dest_id, + msg_count: 0, + time_stamper: cfg.time_stamper, + time_stamp_buf: vec![0; cfg.max_stamp_len], + source_data_buf: vec![ + 0; + RequestId::SIZE_AS_BYTES + + cfg.step_field_width as usize + + cfg.failure_code_field_width as usize + + cfg.max_fail_data_len + ], + } + } + pub fn add_tc(&mut self, req_id: RequestId) -> VerificationToken { VerificationToken::::new(req_id) } @@ -48,7 +103,7 @@ impl VerificationReporter { sender: &mut impl VerificationSender, ) -> Result, SendStoredTmError> { let tm = self - .create_tm(1, 1) + .create_pus_verif_tm(1, 1, &token.req_id) .map_err(|e| SendStoredTmError::TimeStampError(e))?; sender.send_verification_tm(tm)?; self.msg_count += 1; @@ -58,8 +113,18 @@ impl VerificationReporter { }) } - pub fn acceptance_failure(self, _token: VerificationToken) { - unimplemented!(); + pub fn acceptance_failure( + mut self, + token: VerificationToken, + sender: &mut impl VerificationSender, + _failure_notice: &[u8], + ) -> Result<(), SendStoredTmError> { + let tm = self + .create_pus_verif_tm(1, 2, &token.req_id) + .map_err(|e| SendStoredTmError::TimeStampError(e))?; + sender.send_verification_tm(tm)?; + self.msg_count += 1; + Ok(()) } pub fn start_success( @@ -91,7 +156,14 @@ impl VerificationReporter { unimplemented!(); } - fn create_tm(&mut self, service: u8, subservice: u8) -> Result { + fn create_pus_verif_tm( + &mut self, + service: u8, + subservice: u8, + req_id: &RequestId, + ) -> Result { + let source_data_len = size_of::(); + req_id.to_bytes(&mut self.source_data_buf[0..source_data_len]); let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap(); // I think it is okay to panic here. This error should never happen, I consider // this a configuration error. @@ -104,7 +176,12 @@ impl VerificationReporter { self.dest_id, &self.time_stamp_buf, ); - Ok(PusTm::new(&mut sp_header, tm_sec_header, None, true)) + Ok(PusTm::new( + &mut sp_header, + tm_sec_header, + Some(&self.source_data_buf[0..source_data_len]), + true, + )) } } @@ -128,7 +205,6 @@ impl VerificationToken { #[cfg(test)] mod tests { - use crate::pus::verification::VerificationToken; #[test] pub fn test_basic_type_state() { From 38bdc2155f74ce1818ed5c09df5c6a794281a92c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 3 Sep 2022 18:51:01 +0200 Subject: [PATCH 09/43] first basic verif reporter impl --- fsrc-core/src/pus/mod.rs | 2 + fsrc-core/src/pus/verification.rs | 257 +++++++++++++++++++++++----- fsrc-core/src/tmtc/ccsds_distrib.rs | 6 +- spacepackets | 2 +- 4 files changed, 222 insertions(+), 45 deletions(-) diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs index c18a6cf..a03a16d 100644 --- a/fsrc-core/src/pus/mod.rs +++ b/fsrc-core/src/pus/mod.rs @@ -1,5 +1,6 @@ use crate::pool::StoreError; use spacepackets::time::TimestampError; +use spacepackets::ByteConversionError; pub mod verification; @@ -7,5 +8,6 @@ pub mod verification; pub enum SendStoredTmError { SendError(E), TimeStampError(TimestampError), + ToFromBytesError(ByteConversionError), StoreError(StoreError), } diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 86a6542..13b88d6 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -3,10 +3,11 @@ use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; use core::mem::size_of; +use spacepackets::ecss::EcssEnumeration; use spacepackets::tc::PusTc; -use spacepackets::time::{CcsdsTimeProvider, TimeWriter, TimestampError}; +use spacepackets::time::{CcsdsTimeProvider, TimeWriter}; use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; -use spacepackets::SpHeader; +use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; use std::marker::PhantomData; @@ -75,6 +76,45 @@ impl VerificationReporterCfg { } } +pub struct FailParams<'a, E> { + sender: &'a mut dyn VerificationSender, + failure_code: &'a dyn EcssEnumeration, + failure_data: &'a [u8], +} + +impl<'a, E> FailParams<'a, E> { + pub fn new( + sender: &'a mut impl VerificationSender, + failure_code: &'a impl EcssEnumeration, + failure_data: &'a [u8], + ) -> Self { + Self { + sender, + failure_code, + failure_data, + } + } +} + +pub struct FailParamsWithStep<'a, E> { + bp: FailParams<'a, E>, + step: &'a dyn EcssEnumeration, +} + +impl<'a, E> FailParamsWithStep<'a, E> { + pub fn new( + sender: &'a mut impl VerificationSender, + failure_code: &'a impl EcssEnumeration, + failure_data: &'a [u8], + step: &'a impl EcssEnumeration, + ) -> Self { + Self { + bp: FailParams::new(sender, failure_code, failure_data), + step, + } + } +} + impl VerificationReporter { pub fn new(cfg: VerificationReporterCfg) -> Self { Self { @@ -102,9 +142,8 @@ impl VerificationReporter { token: VerificationToken, sender: &mut impl VerificationSender, ) -> Result, SendStoredTmError> { - let tm = self - .create_pus_verif_tm(1, 1, &token.req_id) - .map_err(|e| SendStoredTmError::TimeStampError(e))?; + let test: Option<&dyn EcssEnumeration> = None; + let tm = self.create_pus_verif_success_tm(1, 1, &token.req_id, test)?; sender.send_verification_tm(tm)?; self.msg_count += 1; Ok(VerificationToken { @@ -116,59 +155,195 @@ impl VerificationReporter { pub fn acceptance_failure( mut self, token: VerificationToken, - sender: &mut impl VerificationSender, - _failure_notice: &[u8], + params: FailParams, ) -> Result<(), SendStoredTmError> { - let tm = self - .create_pus_verif_tm(1, 2, &token.req_id) - .map_err(|e| SendStoredTmError::TimeStampError(e))?; + let tm = self.create_pus_verif_fail_tm( + 1, + 2, + &token.req_id, + None::<&dyn EcssEnumeration>, + params.failure_code, + params.failure_data, + )?; + params.sender.send_verification_tm(tm)?; + self.msg_count += 1; + Ok(()) + } + + pub fn start_success( + &mut self, + token: VerificationToken, + sender: &mut impl VerificationSender, + ) -> Result, SendStoredTmError> { + let tm = + self.create_pus_verif_success_tm(1, 3, &token.req_id, None::<&dyn EcssEnumeration>)?; + sender.send_verification_tm(tm)?; + self.msg_count += 1; + Ok(VerificationToken { + state: PhantomData, + req_id: token.req_id, + }) + } + + pub fn start_failure( + &mut self, + token: VerificationToken, + params: FailParams, + ) -> Result<(), SendStoredTmError> { + let tm = self.create_pus_verif_fail_tm( + 1, + 4, + &token.req_id, + None::<&dyn EcssEnumeration>, + params.failure_code, + params.failure_data, + )?; + params.sender.send_verification_tm(tm)?; + self.msg_count += 1; + Ok(()) + } + + pub fn step_success( + &mut self, + token: &VerificationToken, + sender: &mut impl VerificationSender, + step: impl EcssEnumeration, + ) -> Result<(), SendStoredTmError> { + let tm = self.create_pus_verif_success_tm(1, 5, &token.req_id, Some(&step))?; sender.send_verification_tm(tm)?; self.msg_count += 1; Ok(()) } - pub fn start_success( + pub fn step_failure( &mut self, token: VerificationToken, - ) -> VerificationToken { - VerificationToken { - state: PhantomData, - req_id: token.req_id, - } + params: FailParamsWithStep, + ) -> Result<(), SendStoredTmError> { + let tm = self.create_pus_verif_fail_tm( + 1, + 6, + &token.req_id, + Some(params.step), + params.bp.failure_code, + params.bp.failure_data, + )?; + params.bp.sender.send_verification_tm(tm)?; + self.msg_count += 1; + Ok(()) } - pub fn start_failure(&mut self, _token: VerificationToken) { - unimplemented!(); + pub fn completion_success( + &mut self, + token: VerificationToken, + sender: &mut impl VerificationSender, + ) -> Result<(), SendStoredTmError> { + let tm = + self.create_pus_verif_success_tm(1, 7, &token.req_id, None::<&dyn EcssEnumeration>)?; + sender.send_verification_tm(tm)?; + self.msg_count += 1; + Ok(()) } - pub fn step_success(&mut self, _token: &VerificationToken) { - unimplemented!(); + pub fn completion_failure( + &mut self, + token: VerificationToken, + params: FailParams, + ) -> Result<(), SendStoredTmError> { + let tm = self.create_pus_verif_fail_tm( + 1, + 8, + &token.req_id, + None::<&dyn EcssEnumeration>, + params.failure_code, + params.failure_data, + )?; + params.sender.send_verification_tm(tm)?; + self.msg_count += 1; + Ok(()) } - pub fn step_failure(&mut self, _token: VerificationToken) { - unimplemented!(); - } - - pub fn completion_success(&mut self, _token: VerificationToken) { - unimplemented!(); - } - pub fn completion_failure(&mut self, _token: VerificationToken) { - unimplemented!(); - } - - fn create_pus_verif_tm( + fn create_pus_verif_success_tm( &mut self, service: u8, subservice: u8, req_id: &RequestId, - ) -> Result { - let source_data_len = size_of::(); - req_id.to_bytes(&mut self.source_data_buf[0..source_data_len]); + step: Option<&(impl EcssEnumeration + ?Sized)>, + ) -> Result> { + let mut source_data_len = size_of::(); + if let Some(step) = step { + source_data_len += step.byte_width() as usize; + } + self.source_buffer_large_enough(source_data_len)?; + let mut idx = 0; + req_id.to_bytes(&mut self.source_data_buf[0..RequestId::SIZE_AS_BYTES]); + idx += RequestId::SIZE_AS_BYTES; + if let Some(step) = step { + // Size check was done beforehand + step.to_bytes(&mut self.source_data_buf[idx..idx + step.byte_width() as usize]) + .unwrap(); + } let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap(); - // I think it is okay to panic here. This error should never happen, I consider - // this a configuration error. self.time_stamper - .write_to_bytes(self.time_stamp_buf.as_mut_slice())?; + .write_to_bytes(self.time_stamp_buf.as_mut_slice()) + .map_err(|e| SendStoredTmError::TimeStampError(e))?; + Ok(self.create_pus_verif_tm_base(service, subservice, &mut sp_header, source_data_len)) + } + + fn create_pus_verif_fail_tm( + &mut self, + service: u8, + subservice: u8, + req_id: &RequestId, + step: Option<&(impl EcssEnumeration + ?Sized)>, + fail_code: &(impl EcssEnumeration + ?Sized), + fail_data: &[u8], + ) -> Result> { + let mut idx = 0; + let mut source_data_len = + RequestId::SIZE_AS_BYTES + fail_code.byte_width() as usize + fail_data.len(); + if let Some(step) = step { + source_data_len += step.byte_width() as usize; + } + self.source_buffer_large_enough(source_data_len)?; + req_id.to_bytes(&mut self.source_data_buf[0..RequestId::SIZE_AS_BYTES]); + idx += RequestId::SIZE_AS_BYTES; + if let Some(step) = step { + // Size check done beforehand + step.to_bytes(&mut self.source_data_buf[idx..idx + step.byte_width() as usize]) + .unwrap(); + } + fail_code + .to_bytes(&mut self.source_data_buf[idx..idx + fail_code.byte_width() as usize]) + .map_err(|e| SendStoredTmError::::ToFromBytesError(e))?; + idx += fail_code.byte_width() as usize; + self.source_data_buf[idx..idx + fail_data.len()].copy_from_slice(fail_data); + let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap(); + self.time_stamper + .write_to_bytes(self.time_stamp_buf.as_mut_slice()) + .map_err(|e| SendStoredTmError::::TimeStampError(e))?; + Ok(self.create_pus_verif_tm_base(service, subservice, &mut sp_header, source_data_len)) + } + + fn source_buffer_large_enough(&self, len: usize) -> Result<(), SendStoredTmError> { + if len > self.source_data_buf.capacity() { + return Err(SendStoredTmError::ToFromBytesError( + ByteConversionError::ToSliceTooSmall(SizeMissmatch { + found: self.source_data_buf.capacity(), + expected: len, + }), + )); + } + Ok(()) + } + + fn create_pus_verif_tm_base( + &mut self, + service: u8, + subservice: u8, + sp_header: &mut SpHeader, + source_data_len: usize, + ) -> PusTm { let tm_sec_header = PusTmSecondaryHeader::new( service, subservice, @@ -176,12 +351,12 @@ impl VerificationReporter { self.dest_id, &self.time_stamp_buf, ); - Ok(PusTm::new( - &mut sp_header, + PusTm::new( + sp_header, tm_sec_header, Some(&self.source_data_buf[0..source_data_len]), true, - )) + ) } } diff --git a/fsrc-core/src/tmtc/ccsds_distrib.rs b/fsrc-core/src/tmtc/ccsds_distrib.rs index a260644..ec084a6 100644 --- a/fsrc-core/src/tmtc/ccsds_distrib.rs +++ b/fsrc-core/src/tmtc/ccsds_distrib.rs @@ -87,7 +87,7 @@ use crate::tmtc::{ReceivesCcsdsTc, ReceivesTc}; use alloc::boxed::Box; use downcast_rs::Downcast; -use spacepackets::{CcsdsPacket, PacketError, SizeMissmatch, SpHeader}; +use spacepackets::{ByteConversionError, CcsdsPacket, SizeMissmatch, SpHeader}; /// Generic trait for a handler or dispatcher object handling CCSDS packets. /// @@ -125,7 +125,7 @@ pub struct CcsdsDistributor { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum CcsdsError { CustomError(E), - PacketError(PacketError), + PacketError(ByteConversionError), } impl ReceivesCcsdsTc for CcsdsDistributor { @@ -142,7 +142,7 @@ impl ReceivesTc for CcsdsDistributor { fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { if tc_raw.len() < 7 { return Err(CcsdsError::PacketError( - PacketError::FromBytesSliceTooSmall(SizeMissmatch { + ByteConversionError::FromSliceTooSmall(SizeMissmatch { found: tc_raw.len(), expected: 7, }), diff --git a/spacepackets b/spacepackets index 96d389a..bb83e67 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit 96d389a6515f763f179c23742a9f578659ae9509 +Subproject commit bb83e67e54619ad160bd3a02eb55993ec39a70df From d721bb4d2c1c149b2009c7ebead5b8a7157e0c93 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 3 Sep 2022 20:55:32 +0200 Subject: [PATCH 10/43] first basic unit test for verification reporter --- fsrc-core/src/pus/mod.rs | 3 + fsrc-core/src/pus/verification.rs | 263 +++++++++++++++++++++++++----- spacepackets | 2 +- 3 files changed, 228 insertions(+), 40 deletions(-) diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs index a03a16d..2c0cdd1 100644 --- a/fsrc-core/src/pus/mod.rs +++ b/fsrc-core/src/pus/mod.rs @@ -1,7 +1,9 @@ use crate::pool::StoreError; +use spacepackets::ecss::PusError; use spacepackets::time::TimestampError; use spacepackets::ByteConversionError; +#[cfg(feature = "alloc")] pub mod verification; #[derive(Debug, Clone)] @@ -9,5 +11,6 @@ pub enum SendStoredTmError { SendError(E), TimeStampError(TimestampError), ToFromBytesError(ByteConversionError), + PusError(PusError), StoreError(StoreError), } diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 13b88d6..869093c 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -1,16 +1,24 @@ +use crate::pool::{LocalPool, StoreAddr}; use crate::pus::SendStoredTmError; -use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; use core::mem::size_of; use spacepackets::ecss::EcssEnumeration; use spacepackets::tc::PusTc; -use spacepackets::time::{CcsdsTimeProvider, TimeWriter}; +use spacepackets::time::CcsdsTimeProvider; use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; use std::marker::PhantomData; +use alloc::sync::Arc; +#[cfg(feature = "std")] +use std::sync::{mpsc, Mutex}; + +#[cfg(feature = "std")] +use std::sync::mpsc::SendError; +use std::sync::MutexGuard; + #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub struct RequestId { version_number: u8, @@ -22,11 +30,23 @@ impl RequestId { const SIZE_AS_BYTES: usize = size_of::(); pub fn to_bytes(&self, buf: &mut [u8]) { - let raw = ((self.version_number as u32) << 31) + let raw = ((self.version_number as u32) << 29) | ((self.packet_id.raw() as u32) << 16) | self.psc.raw() as u32; buf.copy_from_slice(raw.to_be_bytes().as_slice()); } + + pub fn from_bytes(buf: &[u8]) -> Option { + if buf.len() < 4 { + return None; + } + let raw = u32::from_be_bytes(buf[0..Self::SIZE_AS_BYTES].try_into().unwrap()); + Some(Self { + version_number: ((raw >> 29) & 0b111) as u8, + packet_id: PacketId::from(((raw >> 16) & 0xffff) as u16), + psc: PacketSequenceCtrl::from((raw & 0xffff) as u16), + }) + } } impl RequestId { pub fn new(tc: &PusTc) -> Self { @@ -42,19 +62,73 @@ pub trait VerificationSender { fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), SendStoredTmError>; } +#[cfg(feature = "std")] +pub struct StdVerifSender { + pub ignore_poison_error: bool, + tm_store: Arc>, + tx: mpsc::Sender, +} + +#[cfg(feature = "std")] +impl StdVerifSender { + pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { + Self { + ignore_poison_error: true, + tx, + tm_store, + } + } +} + +#[cfg(feature = "std")] +#[derive(Debug, Eq, PartialEq)] +pub enum StdVerifSenderError { + PoisonError, + SendError(SendError), +} + +#[cfg(feature = "std")] +impl VerificationSender for StdVerifSender { + fn send_verification_tm( + &mut self, + tm: PusTm, + ) -> Result<(), SendStoredTmError> { + let operation = |mut mg: MutexGuard| { + let (addr, mut buf) = mg + .free_element(tm.len_packed()) + .map_err(|e| SendStoredTmError::StoreError(e))?; + tm.write_to(&mut buf) + .map_err(|e| SendStoredTmError::PusError(e))?; + self.tx + .send(addr) + .map_err(|e| SendStoredTmError::SendError(StdVerifSenderError::SendError(e)))?; + Ok(()) + }; + match self.tm_store.lock() { + Ok(lock) => operation(lock), + Err(poison_error) => { + if self.ignore_poison_error { + operation(poison_error.into_inner()) + } else { + return Err(SendStoredTmError::SendError( + StdVerifSenderError::PoisonError, + )); + } + } + } + } +} + pub struct VerificationReporter { pub apid: u16, pub dest_id: u16, msg_count: u16, - time_stamper: Box, - time_stamp_buf: Vec, source_data_buf: Vec, } pub struct VerificationReporterCfg { pub apid: u16, pub dest_id: u16, - pub time_stamper: Box, pub step_field_width: u8, pub failure_code_field_width: u8, pub max_fail_data_len: usize, @@ -62,12 +136,11 @@ pub struct VerificationReporterCfg { } impl VerificationReporterCfg { - pub fn new(time_stamper: impl TimeWriter + CcsdsTimeProvider + 'static, apid: u16) -> Self { + pub fn new(time_stamper: impl CcsdsTimeProvider, apid: u16) -> Self { let max_stamp_len = time_stamper.len_as_bytes(); Self { apid, dest_id: 0, - time_stamper: Box::new(time_stamper), step_field_width: size_of::() as u8, failure_code_field_width: size_of::() as u8, max_fail_data_len: 2 * size_of::(), @@ -77,6 +150,7 @@ impl VerificationReporterCfg { } pub struct FailParams<'a, E> { + time_stamp: &'a [u8], sender: &'a mut dyn VerificationSender, failure_code: &'a dyn EcssEnumeration, failure_data: &'a [u8], @@ -85,11 +159,13 @@ pub struct FailParams<'a, E> { impl<'a, E> FailParams<'a, E> { pub fn new( sender: &'a mut impl VerificationSender, + time_stamp: &'a [u8], failure_code: &'a impl EcssEnumeration, failure_data: &'a [u8], ) -> Self { Self { sender, + time_stamp, failure_code, failure_data, } @@ -104,12 +180,13 @@ pub struct FailParamsWithStep<'a, E> { impl<'a, E> FailParamsWithStep<'a, E> { pub fn new( sender: &'a mut impl VerificationSender, + time_stamp: &'a [u8], failure_code: &'a impl EcssEnumeration, failure_data: &'a [u8], step: &'a impl EcssEnumeration, ) -> Self { Self { - bp: FailParams::new(sender, failure_code, failure_data), + bp: FailParams::new(sender, time_stamp, failure_code, failure_data), step, } } @@ -121,8 +198,6 @@ impl VerificationReporter { apid: cfg.apid, dest_id: cfg.dest_id, msg_count: 0, - time_stamper: cfg.time_stamper, - time_stamp_buf: vec![0; cfg.max_stamp_len], source_data_buf: vec![ 0; RequestId::SIZE_AS_BYTES @@ -133,7 +208,11 @@ impl VerificationReporter { } } - pub fn add_tc(&mut self, req_id: RequestId) -> VerificationToken { + pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken { + self.add_tc_with_req_id(RequestId::new(pus_tc)) + } + + pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken { VerificationToken::::new(req_id) } @@ -141,9 +220,15 @@ impl VerificationReporter { &mut self, token: VerificationToken, sender: &mut impl VerificationSender, + time_stamp: &[u8], ) -> Result, SendStoredTmError> { - let test: Option<&dyn EcssEnumeration> = None; - let tm = self.create_pus_verif_success_tm(1, 1, &token.req_id, test)?; + let tm = self.create_pus_verif_success_tm( + 1, + 1, + &token.req_id, + time_stamp, + None::<&dyn EcssEnumeration>, + )?; sender.send_verification_tm(tm)?; self.msg_count += 1; Ok(VerificationToken { @@ -160,6 +245,7 @@ impl VerificationReporter { let tm = self.create_pus_verif_fail_tm( 1, 2, + params.time_stamp, &token.req_id, None::<&dyn EcssEnumeration>, params.failure_code, @@ -173,10 +259,16 @@ impl VerificationReporter { pub fn start_success( &mut self, token: VerificationToken, + time_stamp: &[u8], sender: &mut impl VerificationSender, ) -> Result, SendStoredTmError> { - let tm = - self.create_pus_verif_success_tm(1, 3, &token.req_id, None::<&dyn EcssEnumeration>)?; + let tm = self.create_pus_verif_success_tm( + 1, + 3, + &token.req_id, + time_stamp, + None::<&dyn EcssEnumeration>, + )?; sender.send_verification_tm(tm)?; self.msg_count += 1; Ok(VerificationToken { @@ -193,6 +285,7 @@ impl VerificationReporter { let tm = self.create_pus_verif_fail_tm( 1, 4, + params.time_stamp, &token.req_id, None::<&dyn EcssEnumeration>, params.failure_code, @@ -207,9 +300,10 @@ impl VerificationReporter { &mut self, token: &VerificationToken, sender: &mut impl VerificationSender, + time_stamp: &[u8], step: impl EcssEnumeration, ) -> Result<(), SendStoredTmError> { - let tm = self.create_pus_verif_success_tm(1, 5, &token.req_id, Some(&step))?; + let tm = self.create_pus_verif_success_tm(1, 5, &token.req_id, time_stamp, Some(&step))?; sender.send_verification_tm(tm)?; self.msg_count += 1; Ok(()) @@ -223,6 +317,7 @@ impl VerificationReporter { let tm = self.create_pus_verif_fail_tm( 1, 6, + params.bp.time_stamp, &token.req_id, Some(params.step), params.bp.failure_code, @@ -237,9 +332,15 @@ impl VerificationReporter { &mut self, token: VerificationToken, sender: &mut impl VerificationSender, + time_stamp: &[u8], ) -> Result<(), SendStoredTmError> { - let tm = - self.create_pus_verif_success_tm(1, 7, &token.req_id, None::<&dyn EcssEnumeration>)?; + let tm = self.create_pus_verif_success_tm( + 1, + 7, + &token.req_id, + time_stamp, + None::<&dyn EcssEnumeration>, + )?; sender.send_verification_tm(tm)?; self.msg_count += 1; Ok(()) @@ -253,6 +354,7 @@ impl VerificationReporter { let tm = self.create_pus_verif_fail_tm( 1, 8, + params.time_stamp, &token.req_id, None::<&dyn EcssEnumeration>, params.failure_code, @@ -263,11 +365,12 @@ impl VerificationReporter { Ok(()) } - fn create_pus_verif_success_tm( - &mut self, + fn create_pus_verif_success_tm<'a, E>( + &'a mut self, service: u8, subservice: u8, req_id: &RequestId, + time_stamp: &'a [u8], step: Option<&(impl EcssEnumeration + ?Sized)>, ) -> Result> { let mut source_data_len = size_of::(); @@ -284,16 +387,20 @@ impl VerificationReporter { .unwrap(); } let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap(); - self.time_stamper - .write_to_bytes(self.time_stamp_buf.as_mut_slice()) - .map_err(|e| SendStoredTmError::TimeStampError(e))?; - Ok(self.create_pus_verif_tm_base(service, subservice, &mut sp_header, source_data_len)) + Ok(self.create_pus_verif_tm_base( + service, + subservice, + &mut sp_header, + time_stamp, + source_data_len, + )) } - fn create_pus_verif_fail_tm( - &mut self, + fn create_pus_verif_fail_tm<'a, E>( + &'a mut self, service: u8, subservice: u8, + time_stamp: &'a [u8], req_id: &RequestId, step: Option<&(impl EcssEnumeration + ?Sized)>, fail_code: &(impl EcssEnumeration + ?Sized), @@ -319,10 +426,13 @@ impl VerificationReporter { idx += fail_code.byte_width() as usize; self.source_data_buf[idx..idx + fail_data.len()].copy_from_slice(fail_data); let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap(); - self.time_stamper - .write_to_bytes(self.time_stamp_buf.as_mut_slice()) - .map_err(|e| SendStoredTmError::::TimeStampError(e))?; - Ok(self.create_pus_verif_tm_base(service, subservice, &mut sp_header, source_data_len)) + Ok(self.create_pus_verif_tm_base( + service, + subservice, + &mut sp_header, + time_stamp, + source_data_len, + )) } fn source_buffer_large_enough(&self, len: usize) -> Result<(), SendStoredTmError> { @@ -337,11 +447,12 @@ impl VerificationReporter { Ok(()) } - fn create_pus_verif_tm_base( - &mut self, + fn create_pus_verif_tm_base<'a>( + &'a mut self, service: u8, subservice: u8, sp_header: &mut SpHeader, + time_stamp: &'a [u8], source_data_len: usize, ) -> PusTm { let tm_sec_header = PusTmSecondaryHeader::new( @@ -349,7 +460,7 @@ impl VerificationReporter { subservice, self.msg_count, self.dest_id, - &self.time_stamp_buf, + time_stamp, ); PusTm::new( sp_header, @@ -380,12 +491,86 @@ impl VerificationToken { #[cfg(test)] mod tests { + use crate::pus::verification::{ + RequestId, VerificationReporter, VerificationReporterCfg, VerificationSender, + }; + use crate::pus::SendStoredTmError; + use alloc::vec::Vec; + use spacepackets::ecss::PusPacket; + use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; + use spacepackets::time::{CdsShortTimeProvider, TimeWriter}; + use spacepackets::tm::{PusTm, PusTmSecondaryHeaderT}; + use spacepackets::{CcsdsPacket, SpHeader}; + use std::collections::VecDeque; + const TEST_APID: u16 = 0x02; + + struct TmInfo { + pub subservice: u8, + pub apid: u16, + pub msg_counter: u16, + pub dest_id: u16, + pub time_stamp: [u8; 7], + pub req_id: RequestId, + pub additional_data: Option>, + } + + #[derive(Default)] + struct TestSender { + pub service_queue: VecDeque, + } + + impl VerificationSender<()> for TestSender { + fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), SendStoredTmError<()>> { + assert_eq!(PusPacket::service(&tm), 1); + assert!(tm.source_data().is_some()); + let mut time_stamp = [0; 7]; + time_stamp.clone_from_slice(tm.time_stamp()); + let src_data = tm.source_data().unwrap(); + assert!(src_data.len() >= 4); + let req_id = RequestId::from_bytes(&src_data[0..RequestId::SIZE_AS_BYTES]).unwrap(); + let mut vec = None; + if src_data.len() > 4 { + let mut new_vec = Vec::new(); + new_vec.extend_from_slice(&src_data[RequestId::SIZE_AS_BYTES..]); + vec = Some(new_vec); + } + self.service_queue.push_back(TmInfo { + subservice: PusPacket::subservice(&tm), + apid: tm.apid(), + msg_counter: tm.msg_counter(), + dest_id: tm.dest_id(), + time_stamp, + req_id, + additional_data: vec, + }); + Ok(()) + } + } #[test] - pub fn test_basic_type_state() { - //let mut reporter = VerificationToken::new(); - //let mut accepted = reporter.acceptance_success(); - //let started = accepted.start_success(); - //started.completion_success(); + pub fn test_basic() { + let time_stamper = CdsShortTimeProvider::default(); + let cfg = VerificationReporterCfg::new(time_stamper, 0x02); + let mut reporter = VerificationReporter::new(cfg); + let mut sph = SpHeader::tc(TEST_APID, 0x34, 0).unwrap(); + let tc_header = PusTcSecondaryHeader::new_simple(17, 1); + let pus_tc = PusTc::new(&mut sph, tc_header, None, true); + let verif_token = reporter.add_tc(&pus_tc); + let req_id = RequestId::new(&pus_tc); + let mut stamp_buf = [0; 7]; + time_stamper.write_to_bytes(&mut stamp_buf).unwrap(); + let mut sender = TestSender::default(); + reporter + .acceptance_success(verif_token, &mut sender, &stamp_buf) + .expect("Sending acceptance success failed"); + assert_eq!(sender.service_queue.len(), 1); + let info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info.subservice, 1); + assert_eq!(info.time_stamp, [0; 7]); + assert_eq!(info.dest_id, 0); + assert_eq!(info.apid, TEST_APID); + assert_eq!(info.msg_counter, 0); + assert_eq!(info.additional_data, None); + assert_eq!(info.req_id, req_id); } } diff --git a/spacepackets b/spacepackets index bb83e67..c72c5ad 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit bb83e67e54619ad160bd3a02eb55993ec39a70df +Subproject commit c72c5ad4aaca277754e94a16377a7d4a6eff5d8e From 4fa79cf7cdbf2668bb40e85000b107970a509e15 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 4 Sep 2022 22:24:36 +0200 Subject: [PATCH 11/43] various improvements --- fsrc-core/src/pus/mod.rs | 14 -- fsrc-core/src/pus/verification.rs | 249 ++++++++++++++++-------------- 2 files changed, 131 insertions(+), 132 deletions(-) diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs index 2c0cdd1..6abb03a 100644 --- a/fsrc-core/src/pus/mod.rs +++ b/fsrc-core/src/pus/mod.rs @@ -1,16 +1,2 @@ -use crate::pool::StoreError; -use spacepackets::ecss::PusError; -use spacepackets::time::TimestampError; -use spacepackets::ByteConversionError; - #[cfg(feature = "alloc")] pub mod verification; - -#[derive(Debug, Clone)] -pub enum SendStoredTmError { - SendError(E), - TimeStampError(TimestampError), - ToFromBytesError(ByteConversionError), - PusError(PusError), - StoreError(StoreError), -} diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 869093c..c5ef6e6 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -1,11 +1,10 @@ -use crate::pool::{LocalPool, StoreAddr}; -use crate::pus::SendStoredTmError; +use crate::pool::{LocalPool, StoreAddr, StoreError}; use alloc::vec; use alloc::vec::Vec; use core::mem::size_of; -use spacepackets::ecss::EcssEnumeration; +use spacepackets::ecss::{EcssEnumeration, PusError}; use spacepackets::tc::PusTc; -use spacepackets::time::CcsdsTimeProvider; +use spacepackets::time::{CcsdsTimeProvider, TimestampError}; use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; @@ -26,6 +25,18 @@ pub struct RequestId { psc: PacketSequenceCtrl, } +#[derive(Debug, Clone)] +pub enum VerificationError { + SendError(E), + TimeStampError(TimestampError), + ByteConversionError(ByteConversionError), + PusError(PusError), + StoreError(StoreError), +} + +#[derive(Debug, Clone)] +pub struct VerificationErrorWithToken(VerificationError, VerificationToken); + impl RequestId { const SIZE_AS_BYTES: usize = size_of::(); @@ -59,7 +70,7 @@ impl RequestId { } pub trait VerificationSender { - fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), SendStoredTmError>; + fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError>; } #[cfg(feature = "std")] @@ -92,16 +103,15 @@ impl VerificationSender for StdVerifSender { fn send_verification_tm( &mut self, tm: PusTm, - ) -> Result<(), SendStoredTmError> { + ) -> Result<(), VerificationError> { let operation = |mut mg: MutexGuard| { - let (addr, mut buf) = mg + let (addr, buf) = mg .free_element(tm.len_packed()) - .map_err(|e| SendStoredTmError::StoreError(e))?; - tm.write_to(&mut buf) - .map_err(|e| SendStoredTmError::PusError(e))?; + .map_err(VerificationError::StoreError)?; + tm.write_to(buf).map_err(VerificationError::PusError)?; self.tx .send(addr) - .map_err(|e| SendStoredTmError::SendError(StdVerifSenderError::SendError(e)))?; + .map_err(|e| VerificationError::SendError(StdVerifSenderError::SendError(e)))?; Ok(()) }; match self.tm_store.lock() { @@ -110,9 +120,9 @@ impl VerificationSender for StdVerifSender { if self.ignore_poison_error { operation(poison_error.into_inner()) } else { - return Err(SendStoredTmError::SendError( + Err(VerificationError::SendError( StdVerifSenderError::PoisonError, - )); + )) } } } @@ -149,22 +159,19 @@ impl VerificationReporterCfg { } } -pub struct FailParams<'a, E> { +pub struct FailParams<'a> { time_stamp: &'a [u8], - sender: &'a mut dyn VerificationSender, failure_code: &'a dyn EcssEnumeration, failure_data: &'a [u8], } -impl<'a, E> FailParams<'a, E> { +impl<'a> FailParams<'a> { pub fn new( - sender: &'a mut impl VerificationSender, time_stamp: &'a [u8], failure_code: &'a impl EcssEnumeration, failure_data: &'a [u8], ) -> Self { Self { - sender, time_stamp, failure_code, failure_data, @@ -172,21 +179,20 @@ impl<'a, E> FailParams<'a, E> { } } -pub struct FailParamsWithStep<'a, E> { - bp: FailParams<'a, E>, +pub struct FailParamsWithStep<'a> { + bp: FailParams<'a>, step: &'a dyn EcssEnumeration, } -impl<'a, E> FailParamsWithStep<'a, E> { +impl<'a> FailParamsWithStep<'a> { pub fn new( - sender: &'a mut impl VerificationSender, time_stamp: &'a [u8], failure_code: &'a impl EcssEnumeration, failure_data: &'a [u8], step: &'a impl EcssEnumeration, ) -> Self { Self { - bp: FailParams::new(sender, time_stamp, failure_code, failure_data), + bp: FailParams::new(time_stamp, failure_code, failure_data), step, } } @@ -221,15 +227,19 @@ impl VerificationReporter { token: VerificationToken, sender: &mut impl VerificationSender, time_stamp: &[u8], - ) -> Result, SendStoredTmError> { - let tm = self.create_pus_verif_success_tm( - 1, - 1, - &token.req_id, - time_stamp, - None::<&dyn EcssEnumeration>, - )?; - sender.send_verification_tm(tm)?; + ) -> Result, VerificationErrorWithToken> { + let tm = self + .create_pus_verif_success_tm( + 1, + 1, + &token.req_id, + time_stamp, + None::<&dyn EcssEnumeration>, + ) + .map_err(|e| VerificationErrorWithToken(e, token))?; + sender + .send_verification_tm(tm) + .map_err(|e| VerificationErrorWithToken(e, token))?; self.msg_count += 1; Ok(VerificationToken { state: PhantomData, @@ -240,18 +250,15 @@ impl VerificationReporter { pub fn acceptance_failure( mut self, token: VerificationToken, - params: FailParams, - ) -> Result<(), SendStoredTmError> { - let tm = self.create_pus_verif_fail_tm( - 1, - 2, - params.time_stamp, - &token.req_id, - None::<&dyn EcssEnumeration>, - params.failure_code, - params.failure_data, - )?; - params.sender.send_verification_tm(tm)?; + sender: &mut impl VerificationSender, + params: FailParams, + ) -> Result<(), VerificationErrorWithToken> { + let tm = self + .create_pus_verif_fail_tm(1, 2, &token.req_id, ¶ms, None::<&dyn EcssEnumeration>) + .map_err(|e| VerificationErrorWithToken(e, token))?; + sender + .send_verification_tm(tm) + .map_err(|e| VerificationErrorWithToken(e, token))?; self.msg_count += 1; Ok(()) } @@ -259,17 +266,21 @@ impl VerificationReporter { pub fn start_success( &mut self, token: VerificationToken, - time_stamp: &[u8], sender: &mut impl VerificationSender, - ) -> Result, SendStoredTmError> { - let tm = self.create_pus_verif_success_tm( - 1, - 3, - &token.req_id, - time_stamp, - None::<&dyn EcssEnumeration>, - )?; - sender.send_verification_tm(tm)?; + time_stamp: &[u8], + ) -> Result, VerificationErrorWithToken> { + let tm = self + .create_pus_verif_success_tm( + 1, + 3, + &token.req_id, + time_stamp, + None::<&dyn EcssEnumeration>, + ) + .map_err(|e| VerificationErrorWithToken(e, token))?; + sender + .send_verification_tm(tm) + .map_err(|e| VerificationErrorWithToken(e, token))?; self.msg_count += 1; Ok(VerificationToken { state: PhantomData, @@ -280,18 +291,15 @@ impl VerificationReporter { pub fn start_failure( &mut self, token: VerificationToken, - params: FailParams, - ) -> Result<(), SendStoredTmError> { - let tm = self.create_pus_verif_fail_tm( - 1, - 4, - params.time_stamp, - &token.req_id, - None::<&dyn EcssEnumeration>, - params.failure_code, - params.failure_data, - )?; - params.sender.send_verification_tm(tm)?; + sender: &mut impl VerificationSender, + params: FailParams, + ) -> Result<(), VerificationErrorWithToken> { + let tm = self + .create_pus_verif_fail_tm(1, 4, &token.req_id, ¶ms, None::<&dyn EcssEnumeration>) + .map_err(|e| VerificationErrorWithToken(e, token))?; + sender + .send_verification_tm(tm) + .map_err(|e| VerificationErrorWithToken(e, token))?; self.msg_count += 1; Ok(()) } @@ -302,7 +310,7 @@ impl VerificationReporter { sender: &mut impl VerificationSender, time_stamp: &[u8], step: impl EcssEnumeration, - ) -> Result<(), SendStoredTmError> { + ) -> Result<(), VerificationError> { let tm = self.create_pus_verif_success_tm(1, 5, &token.req_id, time_stamp, Some(&step))?; sender.send_verification_tm(tm)?; self.msg_count += 1; @@ -312,18 +320,15 @@ impl VerificationReporter { pub fn step_failure( &mut self, token: VerificationToken, - params: FailParamsWithStep, - ) -> Result<(), SendStoredTmError> { - let tm = self.create_pus_verif_fail_tm( - 1, - 6, - params.bp.time_stamp, - &token.req_id, - Some(params.step), - params.bp.failure_code, - params.bp.failure_data, - )?; - params.bp.sender.send_verification_tm(tm)?; + sender: &mut impl VerificationSender, + params: FailParamsWithStep, + ) -> Result<(), VerificationErrorWithToken> { + let tm = self + .create_pus_verif_fail_tm(1, 6, &token.req_id, ¶ms.bp, Some(params.step)) + .map_err(|e| VerificationErrorWithToken(e, token))?; + sender + .send_verification_tm(tm) + .map_err(|e| VerificationErrorWithToken(e, token))?; self.msg_count += 1; Ok(()) } @@ -333,15 +338,19 @@ impl VerificationReporter { token: VerificationToken, sender: &mut impl VerificationSender, time_stamp: &[u8], - ) -> Result<(), SendStoredTmError> { - let tm = self.create_pus_verif_success_tm( - 1, - 7, - &token.req_id, - time_stamp, - None::<&dyn EcssEnumeration>, - )?; - sender.send_verification_tm(tm)?; + ) -> Result<(), VerificationErrorWithToken> { + let tm = self + .create_pus_verif_success_tm( + 1, + 7, + &token.req_id, + time_stamp, + None::<&dyn EcssEnumeration>, + ) + .map_err(|e| VerificationErrorWithToken(e, token))?; + sender + .send_verification_tm(tm) + .map_err(|e| VerificationErrorWithToken(e, token))?; self.msg_count += 1; Ok(()) } @@ -349,18 +358,15 @@ impl VerificationReporter { pub fn completion_failure( &mut self, token: VerificationToken, - params: FailParams, - ) -> Result<(), SendStoredTmError> { - let tm = self.create_pus_verif_fail_tm( - 1, - 8, - params.time_stamp, - &token.req_id, - None::<&dyn EcssEnumeration>, - params.failure_code, - params.failure_data, - )?; - params.sender.send_verification_tm(tm)?; + sender: &mut impl VerificationSender, + params: FailParams, + ) -> Result<(), VerificationErrorWithToken> { + let tm = self + .create_pus_verif_fail_tm(1, 8, &token.req_id, ¶ms, None::<&dyn EcssEnumeration>) + .map_err(|e| VerificationErrorWithToken(e, token))?; + sender + .send_verification_tm(tm) + .map_err(|e| VerificationErrorWithToken(e, token))?; self.msg_count += 1; Ok(()) } @@ -372,7 +378,7 @@ impl VerificationReporter { req_id: &RequestId, time_stamp: &'a [u8], step: Option<&(impl EcssEnumeration + ?Sized)>, - ) -> Result> { + ) -> Result> { let mut source_data_len = size_of::(); if let Some(step) = step { source_data_len += step.byte_width() as usize; @@ -400,15 +406,14 @@ impl VerificationReporter { &'a mut self, service: u8, subservice: u8, - time_stamp: &'a [u8], req_id: &RequestId, + params: &'a FailParams, step: Option<&(impl EcssEnumeration + ?Sized)>, - fail_code: &(impl EcssEnumeration + ?Sized), - fail_data: &[u8], - ) -> Result> { + ) -> Result> { let mut idx = 0; - let mut source_data_len = - RequestId::SIZE_AS_BYTES + fail_code.byte_width() as usize + fail_data.len(); + let mut source_data_len = RequestId::SIZE_AS_BYTES + + params.failure_code.byte_width() as usize + + params.failure_data.len(); if let Some(step) = step { source_data_len += step.byte_width() as usize; } @@ -420,24 +425,28 @@ impl VerificationReporter { step.to_bytes(&mut self.source_data_buf[idx..idx + step.byte_width() as usize]) .unwrap(); } - fail_code - .to_bytes(&mut self.source_data_buf[idx..idx + fail_code.byte_width() as usize]) - .map_err(|e| SendStoredTmError::::ToFromBytesError(e))?; - idx += fail_code.byte_width() as usize; - self.source_data_buf[idx..idx + fail_data.len()].copy_from_slice(fail_data); + params + .failure_code + .to_bytes( + &mut self.source_data_buf[idx..idx + params.failure_code.byte_width() as usize], + ) + .map_err(|e| VerificationError::::ByteConversionError(e))?; + idx += params.failure_code.byte_width() as usize; + self.source_data_buf[idx..idx + params.failure_data.len()] + .copy_from_slice(params.failure_data); let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap(); Ok(self.create_pus_verif_tm_base( service, subservice, &mut sp_header, - time_stamp, + params.time_stamp, source_data_len, )) } - fn source_buffer_large_enough(&self, len: usize) -> Result<(), SendStoredTmError> { + fn source_buffer_large_enough(&self, len: usize) -> Result<(), VerificationError> { if len > self.source_data_buf.capacity() { - return Err(SendStoredTmError::ToFromBytesError( + return Err(VerificationError::ByteConversionError( ByteConversionError::ToSliceTooSmall(SizeMissmatch { found: self.source_data_buf.capacity(), expected: len, @@ -471,13 +480,17 @@ impl VerificationReporter { } } +#[derive(Debug, Clone, Copy)] pub struct VerificationToken { state: PhantomData, req_id: RequestId, } +#[derive(Copy, Clone, Debug)] pub struct StateNone; +#[derive(Copy, Clone, Debug)] pub struct StateAccepted; +#[derive(Copy, Clone, Debug)] pub struct StateStarted; impl VerificationToken { @@ -492,9 +505,9 @@ impl VerificationToken { #[cfg(test)] mod tests { use crate::pus::verification::{ - RequestId, VerificationReporter, VerificationReporterCfg, VerificationSender, + RequestId, VerificationError, VerificationReporter, VerificationReporterCfg, + VerificationSender, }; - use crate::pus::SendStoredTmError; use alloc::vec::Vec; use spacepackets::ecss::PusPacket; use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; @@ -521,7 +534,7 @@ mod tests { } impl VerificationSender<()> for TestSender { - fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), SendStoredTmError<()>> { + fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError<()>> { assert_eq!(PusPacket::service(&tm), 1); assert!(tm.source_data().is_some()); let mut time_stamp = [0; 7]; From 57a838de82f09b806a1a0434d21220939a984b70 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 5 Sep 2022 00:20:32 +0200 Subject: [PATCH 12/43] this is a sufficient solution --- fsrc-core/src/pus/verification.rs | 346 ++++++++++++++++++++---------- 1 file changed, 238 insertions(+), 108 deletions(-) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index c5ef6e6..5ce9e00 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -1,22 +1,24 @@ use crate::pool::{LocalPool, StoreAddr, StoreError}; +use alloc::boxed::Box; +use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; +use core::marker::PhantomData; use core::mem::size_of; +use delegate::delegate; use spacepackets::ecss::{EcssEnumeration, PusError}; use spacepackets::tc::PusTc; use spacepackets::time::{CcsdsTimeProvider, TimestampError}; use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; -use std::marker::PhantomData; - -use alloc::sync::Arc; -#[cfg(feature = "std")] -use std::sync::{mpsc, Mutex}; #[cfg(feature = "std")] use std::sync::mpsc::SendError; +#[cfg(feature = "std")] use std::sync::MutexGuard; +#[cfg(feature = "std")] +use std::sync::{mpsc, Mutex}; #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub struct RequestId { @@ -25,18 +27,6 @@ pub struct RequestId { psc: PacketSequenceCtrl, } -#[derive(Debug, Clone)] -pub enum VerificationError { - SendError(E), - TimeStampError(TimestampError), - ByteConversionError(ByteConversionError), - PusError(PusError), - StoreError(StoreError), -} - -#[derive(Debug, Clone)] -pub struct VerificationErrorWithToken(VerificationError, VerificationToken); - impl RequestId { const SIZE_AS_BYTES: usize = size_of::(); @@ -69,73 +59,43 @@ impl RequestId { } } +#[derive(Debug, Clone)] +pub enum VerificationError { + SendError(E), + TimeStampError(TimestampError), + ByteConversionError(ByteConversionError), + PusError(PusError), +} + +#[derive(Debug, Clone)] +pub struct VerificationErrorWithToken(VerificationError, VerificationToken); + pub trait VerificationSender { fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError>; } -#[cfg(feature = "std")] -pub struct StdVerifSender { - pub ignore_poison_error: bool, - tm_store: Arc>, - tx: mpsc::Sender, +#[derive(Debug, Clone, Copy)] +pub struct VerificationToken { + state: PhantomData, + req_id: RequestId, } -#[cfg(feature = "std")] -impl StdVerifSender { - pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { - Self { - ignore_poison_error: true, - tx, - tm_store, +#[derive(Copy, Clone, Debug)] +pub struct StateNone; +#[derive(Copy, Clone, Debug)] +pub struct StateAccepted; +#[derive(Copy, Clone, Debug)] +pub struct StateStarted; + +impl VerificationToken { + fn new(req_id: RequestId) -> VerificationToken { + VerificationToken { + state: PhantomData, + req_id, } } } -#[cfg(feature = "std")] -#[derive(Debug, Eq, PartialEq)] -pub enum StdVerifSenderError { - PoisonError, - SendError(SendError), -} - -#[cfg(feature = "std")] -impl VerificationSender for StdVerifSender { - fn send_verification_tm( - &mut self, - tm: PusTm, - ) -> Result<(), VerificationError> { - let operation = |mut mg: MutexGuard| { - let (addr, buf) = mg - .free_element(tm.len_packed()) - .map_err(VerificationError::StoreError)?; - tm.write_to(buf).map_err(VerificationError::PusError)?; - self.tx - .send(addr) - .map_err(|e| VerificationError::SendError(StdVerifSenderError::SendError(e)))?; - Ok(()) - }; - match self.tm_store.lock() { - Ok(lock) => operation(lock), - Err(poison_error) => { - if self.ignore_poison_error { - operation(poison_error.into_inner()) - } else { - Err(VerificationError::SendError( - StdVerifSenderError::PoisonError, - )) - } - } - } - } -} - -pub struct VerificationReporter { - pub apid: u16, - pub dest_id: u16, - msg_count: u16, - source_data_buf: Vec, -} - pub struct VerificationReporterCfg { pub apid: u16, pub dest_id: u16, @@ -162,14 +122,14 @@ impl VerificationReporterCfg { pub struct FailParams<'a> { time_stamp: &'a [u8], failure_code: &'a dyn EcssEnumeration, - failure_data: &'a [u8], + failure_data: Option<&'a [u8]>, } impl<'a> FailParams<'a> { pub fn new( time_stamp: &'a [u8], failure_code: &'a impl EcssEnumeration, - failure_data: &'a [u8], + failure_data: Option<&'a [u8]>, ) -> Self { Self { time_stamp, @@ -188,7 +148,7 @@ impl<'a> FailParamsWithStep<'a> { pub fn new( time_stamp: &'a [u8], failure_code: &'a impl EcssEnumeration, - failure_data: &'a [u8], + failure_data: Option<&'a [u8]>, step: &'a impl EcssEnumeration, ) -> Self { Self { @@ -198,6 +158,13 @@ impl<'a> FailParamsWithStep<'a> { } } +pub struct VerificationReporter { + pub apid: u16, + pub dest_id: u16, + msg_count: u16, + source_data_buf: Vec, +} + impl VerificationReporter { pub fn new(cfg: VerificationReporterCfg) -> Self { Self { @@ -225,7 +192,7 @@ impl VerificationReporter { pub fn acceptance_success( &mut self, token: VerificationToken, - sender: &mut impl VerificationSender, + sender: &mut (impl VerificationSender + ?Sized), time_stamp: &[u8], ) -> Result, VerificationErrorWithToken> { let tm = self @@ -250,7 +217,7 @@ impl VerificationReporter { pub fn acceptance_failure( mut self, token: VerificationToken, - sender: &mut impl VerificationSender, + sender: &mut (impl VerificationSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -266,7 +233,7 @@ impl VerificationReporter { pub fn start_success( &mut self, token: VerificationToken, - sender: &mut impl VerificationSender, + sender: &mut (impl VerificationSender + ?Sized), time_stamp: &[u8], ) -> Result, VerificationErrorWithToken> { let tm = self @@ -291,7 +258,7 @@ impl VerificationReporter { pub fn start_failure( &mut self, token: VerificationToken, - sender: &mut impl VerificationSender, + sender: &mut (impl VerificationSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -307,7 +274,7 @@ impl VerificationReporter { pub fn step_success( &mut self, token: &VerificationToken, - sender: &mut impl VerificationSender, + sender: &mut (impl VerificationSender + ?Sized), time_stamp: &[u8], step: impl EcssEnumeration, ) -> Result<(), VerificationError> { @@ -320,7 +287,7 @@ impl VerificationReporter { pub fn step_failure( &mut self, token: VerificationToken, - sender: &mut impl VerificationSender, + sender: &mut (impl VerificationSender + ?Sized), params: FailParamsWithStep, ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -336,7 +303,7 @@ impl VerificationReporter { pub fn completion_success( &mut self, token: VerificationToken, - sender: &mut impl VerificationSender, + sender: &mut (impl VerificationSender + ?Sized), time_stamp: &[u8], ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -358,7 +325,7 @@ impl VerificationReporter { pub fn completion_failure( &mut self, token: VerificationToken, - sender: &mut impl VerificationSender, + sender: &mut (impl VerificationSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -411,12 +378,14 @@ impl VerificationReporter { step: Option<&(impl EcssEnumeration + ?Sized)>, ) -> Result> { let mut idx = 0; - let mut source_data_len = RequestId::SIZE_AS_BYTES - + params.failure_code.byte_width() as usize - + params.failure_data.len(); + let mut source_data_len = + RequestId::SIZE_AS_BYTES + params.failure_code.byte_width() as usize; if let Some(step) = step { source_data_len += step.byte_width() as usize; } + if let Some(failure_data) = params.failure_data { + source_data_len += failure_data.len(); + } self.source_buffer_large_enough(source_data_len)?; req_id.to_bytes(&mut self.source_data_buf[0..RequestId::SIZE_AS_BYTES]); idx += RequestId::SIZE_AS_BYTES; @@ -432,8 +401,9 @@ impl VerificationReporter { ) .map_err(|e| VerificationError::::ByteConversionError(e))?; idx += params.failure_code.byte_width() as usize; - self.source_data_buf[idx..idx + params.failure_data.len()] - .copy_from_slice(params.failure_data); + if let Some(failure_data) = params.failure_data { + self.source_data_buf[idx..idx + failure_data.len()].copy_from_slice(failure_data); + } let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap(); Ok(self.create_pus_verif_tm_base( service, @@ -480,24 +450,153 @@ impl VerificationReporter { } } -#[derive(Debug, Clone, Copy)] -pub struct VerificationToken { - state: PhantomData, - req_id: RequestId, +pub struct VerificationReporterWithSender { + reporter: VerificationReporter, + sender: Box>, } -#[derive(Copy, Clone, Debug)] -pub struct StateNone; -#[derive(Copy, Clone, Debug)] -pub struct StateAccepted; -#[derive(Copy, Clone, Debug)] -pub struct StateStarted; +impl VerificationReporterWithSender { + pub fn new(cfg: VerificationReporterCfg, sender: Box>) -> Self { + Self { + reporter: VerificationReporter::new(cfg), + sender, + } + } -impl VerificationToken { - fn new(req_id: RequestId) -> VerificationToken { - VerificationToken { - state: PhantomData, - req_id, + delegate! { + to self.reporter { + pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken; + pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken; + } + } + + pub fn acceptance_success( + &mut self, + token: VerificationToken, + time_stamp: &[u8], + ) -> Result, VerificationErrorWithToken> { + self.reporter + .acceptance_success(token, self.sender.as_mut(), time_stamp) + } + + pub fn acceptance_failure( + mut self, + token: VerificationToken, + params: FailParams, + ) -> Result<(), VerificationErrorWithToken> { + self.reporter + .acceptance_failure(token, self.sender.as_mut(), params) + } + + pub fn start_success( + &mut self, + token: VerificationToken, + time_stamp: &[u8], + ) -> Result, VerificationErrorWithToken> { + self.reporter + .start_success(token, self.sender.as_mut(), time_stamp) + } + + pub fn start_failure( + &mut self, + token: VerificationToken, + params: FailParams, + ) -> Result<(), VerificationErrorWithToken> { + self.reporter + .start_failure(token, self.sender.as_mut(), params) + } + + pub fn step_success( + &mut self, + token: &VerificationToken, + time_stamp: &[u8], + step: impl EcssEnumeration, + ) -> Result<(), VerificationError> { + self.reporter + .step_success(token, self.sender.as_mut(), time_stamp, step) + } + + pub fn step_failure( + &mut self, + token: VerificationToken, + params: FailParamsWithStep, + ) -> Result<(), VerificationErrorWithToken> { + self.reporter + .step_failure(token, self.sender.as_mut(), params) + } + + pub fn completion_success( + &mut self, + token: VerificationToken, + time_stamp: &[u8], + ) -> Result<(), VerificationErrorWithToken> { + self.reporter + .completion_success(token, self.sender.as_mut(), time_stamp) + } + + pub fn completion_failure( + &mut self, + token: VerificationToken, + params: FailParams, + ) -> Result<(), VerificationErrorWithToken> { + self.reporter + .completion_failure(token, self.sender.as_mut(), params) + } +} + +#[cfg(feature = "std")] +pub struct StdVerifSender { + pub ignore_poison_error: bool, + tm_store: Arc>, + tx: mpsc::Sender, +} + +#[cfg(feature = "std")] +impl StdVerifSender { + pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { + Self { + ignore_poison_error: true, + tx, + tm_store, + } + } +} + +#[cfg(feature = "std")] +#[derive(Debug, Eq, PartialEq)] +pub enum StdVerifSenderError { + PoisonError, + StoreError(StoreError), + SendError(SendError), +} + +#[cfg(feature = "std")] +impl VerificationSender for StdVerifSender { + fn send_verification_tm( + &mut self, + tm: PusTm, + ) -> Result<(), VerificationError> { + let operation = |mut mg: MutexGuard| { + let (addr, buf) = mg + .free_element(tm.len_packed()) + .map_err(|e| VerificationError::SendError(StdVerifSenderError::StoreError(e)))?; + tm.write_to(buf).map_err(VerificationError::PusError)?; + self.tx + .send(addr) + .map_err(|e| VerificationError::SendError(StdVerifSenderError::SendError(e)))?; + Ok(()) + }; + match self.tm_store.lock() { + Ok(lock) => operation(lock), + Err(poison_error) => { + if self.ignore_poison_error { + operation(poison_error.into_inner()) + } else { + Err(VerificationError::SendError( + StdVerifSenderError::PoisonError, + )) + } + } } } } @@ -505,11 +604,11 @@ impl VerificationToken { #[cfg(test)] mod tests { use crate::pus::verification::{ - RequestId, VerificationError, VerificationReporter, VerificationReporterCfg, + FailParams, RequestId, VerificationError, VerificationReporter, VerificationReporterCfg, VerificationSender, }; use alloc::vec::Vec; - use spacepackets::ecss::PusPacket; + use spacepackets::ecss::{EcssEnumU16, PusPacket}; use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; use spacepackets::time::{CdsShortTimeProvider, TimeWriter}; use spacepackets::tm::{PusTm, PusTmSecondaryHeaderT}; @@ -560,8 +659,9 @@ mod tests { Ok(()) } } + #[test] - pub fn test_basic() { + pub fn test_basic_acceptance_success() { let time_stamper = CdsShortTimeProvider::default(); let cfg = VerificationReporterCfg::new(time_stamper, 0x02); let mut reporter = VerificationReporter::new(cfg); @@ -586,4 +686,34 @@ mod tests { assert_eq!(info.additional_data, None); assert_eq!(info.req_id, req_id); } + + #[test] + pub fn test_basic_acceptance_failure() { + let time_stamper = CdsShortTimeProvider::default(); + let cfg = VerificationReporterCfg::new(time_stamper, 0x02); + let mut reporter = VerificationReporter::new(cfg); + reporter.dest_id = 5; + let mut sph = SpHeader::tc(TEST_APID, 0x34, 0).unwrap(); + let tc_header = PusTcSecondaryHeader::new_simple(17, 1); + let pus_tc = PusTc::new(&mut sph, tc_header, None, true); + let verif_token = reporter.add_tc(&pus_tc); + let req_id = RequestId::new(&pus_tc); + let mut stamp_buf = [1, 2, 3, 4, 5, 6, 7]; + time_stamper.write_to_bytes(&mut stamp_buf).unwrap(); + let mut sender = TestSender::default(); + let fail_code = EcssEnumU16::new(2); + let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, None); + reporter + .acceptance_failure(verif_token, &mut sender, fail_params) + .expect("Sending acceptance success failed"); + assert_eq!(sender.service_queue.len(), 1); + let info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info.subservice, 2); + assert_eq!(info.time_stamp, stamp_buf); + assert_eq!(info.dest_id, 5); + assert_eq!(info.apid, TEST_APID); + assert_eq!(info.msg_counter, 0); + assert_eq!(info.additional_data, Some([0, 2].to_vec())); + assert_eq!(info.req_id, req_id); + } } From 1ee8dd2769be1a2f9fa12466c3c8c1bf96059b52 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 6 Sep 2022 00:17:52 +0200 Subject: [PATCH 13/43] verif handler almost done --- fsrc-core/src/pus/verification.rs | 577 +++++++++++++++++++++++---- fsrc-core/tests/verification_test.rs | 1 + 2 files changed, 510 insertions(+), 68 deletions(-) create mode 100644 fsrc-core/tests/verification_test.rs diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 5ce9e00..9afc557 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -6,6 +6,7 @@ use alloc::vec::Vec; use core::marker::PhantomData; use core::mem::size_of; use delegate::delegate; +use downcast_rs::{impl_downcast, Downcast}; use spacepackets::ecss::{EcssEnumeration, PusError}; use spacepackets::tc::PusTc; use spacepackets::time::{CcsdsTimeProvider, TimestampError}; @@ -70,10 +71,12 @@ pub enum VerificationError { #[derive(Debug, Clone)] pub struct VerificationErrorWithToken(VerificationError, VerificationToken); -pub trait VerificationSender { +pub trait VerificationSender: Downcast { fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError>; } +impl_downcast!(VerificationSender); + #[derive(Debug, Clone, Copy)] pub struct VerificationToken { state: PhantomData, @@ -147,9 +150,9 @@ pub struct FailParamsWithStep<'a> { impl<'a> FailParamsWithStep<'a> { pub fn new( time_stamp: &'a [u8], + step: &'a impl EcssEnumeration, failure_code: &'a impl EcssEnumeration, failure_data: Option<&'a [u8]>, - step: &'a impl EcssEnumeration, ) -> Self { Self { bp: FailParams::new(time_stamp, failure_code, failure_data), @@ -221,7 +224,7 @@ impl VerificationReporter { params: FailParams, ) -> Result<(), VerificationErrorWithToken> { let tm = self - .create_pus_verif_fail_tm(1, 2, &token.req_id, ¶ms, None::<&dyn EcssEnumeration>) + .create_pus_verif_fail_tm(1, 2, &token.req_id, None::<&dyn EcssEnumeration>, ¶ms) .map_err(|e| VerificationErrorWithToken(e, token))?; sender .send_verification_tm(tm) @@ -262,7 +265,7 @@ impl VerificationReporter { params: FailParams, ) -> Result<(), VerificationErrorWithToken> { let tm = self - .create_pus_verif_fail_tm(1, 4, &token.req_id, ¶ms, None::<&dyn EcssEnumeration>) + .create_pus_verif_fail_tm(1, 4, &token.req_id, None::<&dyn EcssEnumeration>, ¶ms) .map_err(|e| VerificationErrorWithToken(e, token))?; sender .send_verification_tm(tm) @@ -273,7 +276,7 @@ impl VerificationReporter { pub fn step_success( &mut self, - token: &VerificationToken, + token: &VerificationToken, sender: &mut (impl VerificationSender + ?Sized), time_stamp: &[u8], step: impl EcssEnumeration, @@ -286,12 +289,12 @@ impl VerificationReporter { pub fn step_failure( &mut self, - token: VerificationToken, + token: VerificationToken, sender: &mut (impl VerificationSender + ?Sized), params: FailParamsWithStep, - ) -> Result<(), VerificationErrorWithToken> { + ) -> Result<(), VerificationErrorWithToken> { let tm = self - .create_pus_verif_fail_tm(1, 6, &token.req_id, ¶ms.bp, Some(params.step)) + .create_pus_verif_fail_tm(1, 6, &token.req_id, Some(params.step), ¶ms.bp) .map_err(|e| VerificationErrorWithToken(e, token))?; sender .send_verification_tm(tm) @@ -302,10 +305,10 @@ impl VerificationReporter { pub fn completion_success( &mut self, - token: VerificationToken, + token: VerificationToken, sender: &mut (impl VerificationSender + ?Sized), time_stamp: &[u8], - ) -> Result<(), VerificationErrorWithToken> { + ) -> Result<(), VerificationErrorWithToken> { let tm = self .create_pus_verif_success_tm( 1, @@ -324,12 +327,12 @@ impl VerificationReporter { pub fn completion_failure( &mut self, - token: VerificationToken, + token: VerificationToken, sender: &mut (impl VerificationSender + ?Sized), params: FailParams, - ) -> Result<(), VerificationErrorWithToken> { + ) -> Result<(), VerificationErrorWithToken> { let tm = self - .create_pus_verif_fail_tm(1, 8, &token.req_id, ¶ms, None::<&dyn EcssEnumeration>) + .create_pus_verif_fail_tm(1, 8, &token.req_id, None::<&dyn EcssEnumeration>, ¶ms) .map_err(|e| VerificationErrorWithToken(e, token))?; sender .send_verification_tm(tm) @@ -374,8 +377,8 @@ impl VerificationReporter { service: u8, subservice: u8, req_id: &RequestId, - params: &'a FailParams, step: Option<&(impl EcssEnumeration + ?Sized)>, + params: &'a FailParams, ) -> Result> { let mut idx = 0; let mut source_data_len = @@ -393,6 +396,7 @@ impl VerificationReporter { // Size check done beforehand step.to_bytes(&mut self.source_data_buf[idx..idx + step.byte_width() as usize]) .unwrap(); + idx += step.byte_width() as usize; } params .failure_code @@ -452,15 +456,19 @@ impl VerificationReporter { pub struct VerificationReporterWithSender { reporter: VerificationReporter, - sender: Box>, + pub sender: Box>, } -impl VerificationReporterWithSender { +impl VerificationReporterWithSender { pub fn new(cfg: VerificationReporterCfg, sender: Box>) -> Self { - Self { - reporter: VerificationReporter::new(cfg), - sender, - } + Self::new_from_reporter(VerificationReporter::new(cfg), sender) + } + + pub fn new_from_reporter( + reporter: VerificationReporter, + sender: Box>, + ) -> Self { + Self { reporter, sender } } delegate! { @@ -508,7 +516,7 @@ impl VerificationReporterWithSender { pub fn step_success( &mut self, - token: &VerificationToken, + token: &VerificationToken, time_stamp: &[u8], step: impl EcssEnumeration, ) -> Result<(), VerificationError> { @@ -518,27 +526,27 @@ impl VerificationReporterWithSender { pub fn step_failure( &mut self, - token: VerificationToken, + token: VerificationToken, params: FailParamsWithStep, - ) -> Result<(), VerificationErrorWithToken> { + ) -> Result<(), VerificationErrorWithToken> { self.reporter .step_failure(token, self.sender.as_mut(), params) } pub fn completion_success( &mut self, - token: VerificationToken, + token: VerificationToken, time_stamp: &[u8], - ) -> Result<(), VerificationErrorWithToken> { + ) -> Result<(), VerificationErrorWithToken> { self.reporter .completion_success(token, self.sender.as_mut(), time_stamp) } pub fn completion_failure( &mut self, - token: VerificationToken, + token: VerificationToken, params: FailParams, - ) -> Result<(), VerificationErrorWithToken> { + ) -> Result<(), VerificationErrorWithToken> { self.reporter .completion_failure(token, self.sender.as_mut(), params) } @@ -604,11 +612,13 @@ impl VerificationSender for StdVerifSender { #[cfg(test)] mod tests { use crate::pus::verification::{ - FailParams, RequestId, VerificationError, VerificationReporter, VerificationReporterCfg, - VerificationSender, + FailParams, FailParamsWithStep, RequestId, StateNone, VerificationError, + VerificationReporter, VerificationReporterCfg, VerificationReporterWithSender, + VerificationSender, VerificationToken, }; + use alloc::boxed::Box; use alloc::vec::Vec; - use spacepackets::ecss::{EcssEnumU16, PusPacket}; + use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusPacket}; use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; use spacepackets::time::{CdsShortTimeProvider, TimeWriter}; use spacepackets::tm::{PusTm, PusTmSecondaryHeaderT}; @@ -616,7 +626,9 @@ mod tests { use std::collections::VecDeque; const TEST_APID: u16 = 0x02; + const EMPTY_STAMP: [u8; 7] = [0; 7]; + #[derive(Debug, Eq, PartialEq)] struct TmInfo { pub subservice: u8, pub apid: u16, @@ -632,6 +644,73 @@ mod tests { pub service_queue: VecDeque, } + struct TestBase<'a> { + #[allow(dead_code)] + ts: CdsShortTimeProvider, + vr: VerificationReporter, + #[allow(dead_code)] + tc: PusTc<'a>, + req_id: RequestId, + } + + struct TestBaseWithHelper<'a, E> { + #[allow(dead_code)] + ts: CdsShortTimeProvider, + helper: VerificationReporterWithSender, + #[allow(dead_code)] + tc: PusTc<'a>, + req_id: RequestId, + } + + fn base_reporter_and_stamper() -> (CdsShortTimeProvider, VerificationReporter) { + let time_stamper = CdsShortTimeProvider::default(); + let cfg = VerificationReporterCfg::new(time_stamper, 0x02); + (time_stamper, VerificationReporter::new(cfg)) + } + + fn base_tc_init(app_data: Option<&[u8]>) -> (PusTc, RequestId) { + let mut sph = SpHeader::tc(TEST_APID, 0x34, 0).unwrap(); + let tc_header = PusTcSecondaryHeader::new_simple(17, 1); + let pus_tc = PusTc::new(&mut sph, tc_header, app_data, true); + let req_id = RequestId::new(&pus_tc); + (pus_tc, req_id) + } + + fn base_init() -> (TestBase<'static>, VerificationToken) { + let (ts, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); + let init_tok = reporter.add_tc(&tc); + ( + TestBase { + ts, + vr: reporter, + tc, + req_id, + }, + init_tok, + ) + } + + fn base_with_helper_init() -> ( + TestBaseWithHelper<'static, ()>, + VerificationToken, + ) { + let (ts, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); + let init_tok = reporter.add_tc(&tc); + let sender = TestSender::default(); + let helper = VerificationReporterWithSender::new_from_reporter(reporter, Box::new(sender)); + ( + TestBaseWithHelper { + ts, + helper: helper, + tc, + req_id, + }, + init_tok, + ) + } + impl VerificationSender<()> for TestSender { fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError<()>> { assert_eq!(PusPacket::service(&tm), 1); @@ -660,44 +739,46 @@ mod tests { } } - #[test] - pub fn test_basic_acceptance_success() { - let time_stamper = CdsShortTimeProvider::default(); - let cfg = VerificationReporterCfg::new(time_stamper, 0x02); - let mut reporter = VerificationReporter::new(cfg); - let mut sph = SpHeader::tc(TEST_APID, 0x34, 0).unwrap(); - let tc_header = PusTcSecondaryHeader::new_simple(17, 1); - let pus_tc = PusTc::new(&mut sph, tc_header, None, true); - let verif_token = reporter.add_tc(&pus_tc); - let req_id = RequestId::new(&pus_tc); - let mut stamp_buf = [0; 7]; - time_stamper.write_to_bytes(&mut stamp_buf).unwrap(); - let mut sender = TestSender::default(); - reporter - .acceptance_success(verif_token, &mut sender, &stamp_buf) - .expect("Sending acceptance success failed"); + fn acceptance_check(sender: &mut TestSender, req_id: &RequestId) { + let cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 1, + dest_id: 0, + apid: TEST_APID, + msg_counter: 0, + additional_data: None, + req_id: req_id.clone(), + }; assert_eq!(sender.service_queue.len(), 1); let info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info.subservice, 1); - assert_eq!(info.time_stamp, [0; 7]); - assert_eq!(info.dest_id, 0); - assert_eq!(info.apid, TEST_APID); - assert_eq!(info.msg_counter, 0); - assert_eq!(info.additional_data, None); - assert_eq!(info.req_id, req_id); + assert_eq!(info, cmp_info); + } + + #[test] + fn test_basic_acceptance_success() { + let (mut b, tok) = base_init(); + let mut sender = TestSender::default(); + b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + acceptance_check(&mut sender, &b.req_id); + } + + #[test] + fn test_basic_acceptance_success_with_helper() { + let (mut b, tok) = base_with_helper_init(); + b.helper + .acceptance_success(tok, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); + acceptance_check(sender, &b.req_id); } #[test] pub fn test_basic_acceptance_failure() { - let time_stamper = CdsShortTimeProvider::default(); - let cfg = VerificationReporterCfg::new(time_stamper, 0x02); - let mut reporter = VerificationReporter::new(cfg); + let (time_stamper, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); reporter.dest_id = 5; - let mut sph = SpHeader::tc(TEST_APID, 0x34, 0).unwrap(); - let tc_header = PusTcSecondaryHeader::new_simple(17, 1); - let pus_tc = PusTc::new(&mut sph, tc_header, None, true); - let verif_token = reporter.add_tc(&pus_tc); - let req_id = RequestId::new(&pus_tc); + let verif_token = reporter.add_tc(&tc); let mut stamp_buf = [1, 2, 3, 4, 5, 6, 7]; time_stamper.write_to_bytes(&mut stamp_buf).unwrap(); let mut sender = TestSender::default(); @@ -706,14 +787,374 @@ mod tests { reporter .acceptance_failure(verif_token, &mut sender, fail_params) .expect("Sending acceptance success failed"); + let cmp_info = TmInfo { + time_stamp: stamp_buf, + subservice: 2, + dest_id: 5, + apid: TEST_APID, + msg_counter: 0, + additional_data: Some([0, 2].to_vec()), + req_id, + }; assert_eq!(sender.service_queue.len(), 1); let info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info.subservice, 2); - assert_eq!(info.time_stamp, stamp_buf); - assert_eq!(info.dest_id, 5); - assert_eq!(info.apid, TEST_APID); - assert_eq!(info.msg_counter, 0); - assert_eq!(info.additional_data, Some([0, 2].to_vec())); - assert_eq!(info.req_id, req_id); + assert_eq!(info, cmp_info); + } + + #[test] + pub fn test_basic_acceptance_failure_with_fail_data() { + let (_, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); + let verif_token = reporter.add_tc(&tc); + let mut sender = TestSender::default(); + let fail_code = EcssEnumU8::new(10); + let fail_data = EcssEnumU32::new(12); + let mut fail_data_raw = [0; 4]; + fail_data.to_bytes(&mut fail_data_raw).unwrap(); + let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); + reporter + .acceptance_failure(verif_token, &mut sender, fail_params) + .expect("Sending acceptance success failed"); + let cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 2, + dest_id: 0, + apid: TEST_APID, + msg_counter: 0, + additional_data: Some([10, 0, 0, 0, 12].to_vec()), + req_id, + }; + assert_eq!(sender.service_queue.len(), 1); + let info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + #[test] + fn test_start_failure() { + let (_, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); + let verif_token = reporter.add_tc(&tc); + let mut sender = TestSender::default(); + let fail_code = EcssEnumU8::new(22); + let fail_data: i32 = -12; + let mut fail_data_raw = [0; 4]; + fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); + let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); + + let accepted_token = reporter + .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let empty = reporter + .start_failure(accepted_token, &mut sender, fail_params) + .expect("Start failure failure"); + assert_eq!(empty, ()); + assert_eq!(sender.service_queue.len(), 2); + let mut cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 1, + dest_id: 0, + apid: TEST_APID, + msg_counter: 0, + additional_data: None, + req_id, + }; + let mut info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 4, + dest_id: 0, + apid: TEST_APID, + msg_counter: 1, + additional_data: Some([&[22], fail_data_raw.as_slice()].concat().to_vec()), + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + #[test] + fn test_steps_success() { + let (_, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); + let verif_token = reporter.add_tc(&tc); + let mut sender = TestSender::default(); + let accepted_token = reporter + .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = reporter + .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let mut empty = reporter + .step_success( + &started_token, + &mut sender, + &EMPTY_STAMP, + EcssEnumU8::new(0), + ) + .expect("Sending step 0 success failed"); + assert_eq!(empty, ()); + empty = reporter + .step_success( + &started_token, + &mut sender, + &EMPTY_STAMP, + EcssEnumU8::new(1), + ) + .expect("Sending step 1 success failed"); + assert_eq!(empty, ()); + let mut cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 1, + dest_id: 0, + apid: TEST_APID, + msg_counter: 0, + additional_data: None, + req_id, + }; + let mut info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + cmp_info = TmInfo { + time_stamp: [0, 1, 0, 1, 0, 1, 0], + subservice: 3, + dest_id: 0, + apid: TEST_APID, + msg_counter: 1, + additional_data: None, + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 5, + dest_id: 0, + apid: TEST_APID, + msg_counter: 2, + additional_data: Some([0].to_vec()), + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 5, + dest_id: 0, + apid: TEST_APID, + msg_counter: 3, + additional_data: Some([1].to_vec()), + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + #[test] + fn test_step_failure() { + let (_, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); + let verif_token = reporter.add_tc(&tc); + let mut sender = TestSender::default(); + let fail_code = EcssEnumU32::new(0x1020); + let fail_data: f32 = -22.3232; + let mut fail_data_raw = [0; 4]; + fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); + let fail_step = EcssEnumU8::new(1); + let fail_params = FailParamsWithStep::new( + &EMPTY_STAMP, + &fail_step, + &fail_code, + Some(fail_data_raw.as_slice()), + ); + + let accepted_token = reporter + .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = reporter + .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let mut empty = reporter + .step_success( + &started_token, + &mut sender, + &EMPTY_STAMP, + EcssEnumU8::new(0), + ) + .expect("Sending completion success failed"); + assert_eq!(empty, ()); + empty = reporter + .step_failure(started_token, &mut sender, fail_params) + .expect("Step failure failed"); + assert_eq!(empty, ()); + assert_eq!(sender.service_queue.len(), 4); + + let mut cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 1, + dest_id: 0, + apid: TEST_APID, + msg_counter: 0, + additional_data: None, + req_id, + }; + let mut info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + time_stamp: [0, 1, 0, 1, 0, 1, 0], + subservice: 3, + dest_id: 0, + apid: TEST_APID, + msg_counter: 1, + additional_data: None, + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 5, + dest_id: 0, + apid: TEST_APID, + msg_counter: 2, + additional_data: Some([0].to_vec()), + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 6, + dest_id: 0, + apid: TEST_APID, + msg_counter: 3, + additional_data: Some( + [ + [1].as_slice(), + &[0, 0, 0x10, 0x20], + fail_data_raw.as_slice(), + ] + .concat() + .to_vec(), + ), + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + #[test] + fn test_completion_failure() { + let (_, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); + let verif_token = reporter.add_tc(&tc); + let mut sender = TestSender::default(); + let fail_code = EcssEnumU32::new(0x1020); + let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, None); + + let accepted_token = reporter + .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = reporter + .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let empty = reporter + .completion_failure(started_token, &mut sender, fail_params) + .expect("Completion failure"); + assert_eq!(empty, ()); + + assert_eq!(sender.service_queue.len(), 3); + + let mut cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 1, + dest_id: 0, + apid: TEST_APID, + msg_counter: 0, + additional_data: None, + req_id, + }; + let mut info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + time_stamp: [0, 1, 0, 1, 0, 1, 0], + subservice: 3, + dest_id: 0, + apid: TEST_APID, + msg_counter: 1, + additional_data: None, + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + + cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 8, + dest_id: 0, + apid: TEST_APID, + msg_counter: 2, + additional_data: Some([0, 0, 0x10, 0x20].to_vec()), + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + } + + #[test] + fn test_complete_success_sequence() { + let (_, mut reporter) = base_reporter_and_stamper(); + let (tc, req_id) = base_tc_init(None); + let verif_token = reporter.add_tc(&tc); + let mut sender = TestSender::default(); + let accepted_token = reporter + .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = reporter + .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let empty = reporter + .completion_success(started_token, &mut sender, &EMPTY_STAMP) + .expect("Sending completion success failed"); + assert_eq!(empty, ()); + assert_eq!(sender.service_queue.len(), 3); + let cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 1, + dest_id: 0, + apid: TEST_APID, + msg_counter: 0, + additional_data: None, + req_id, + }; + let mut info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + + let cmp_info = TmInfo { + time_stamp: [0, 1, 0, 1, 0, 1, 0], + subservice: 3, + dest_id: 0, + apid: TEST_APID, + msg_counter: 1, + additional_data: None, + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); + let cmp_info = TmInfo { + time_stamp: EMPTY_STAMP, + subservice: 7, + dest_id: 0, + apid: TEST_APID, + msg_counter: 2, + additional_data: None, + req_id, + }; + info = sender.service_queue.pop_front().unwrap(); + assert_eq!(info, cmp_info); } } diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fsrc-core/tests/verification_test.rs @@ -0,0 +1 @@ + From 093249815d36295aed32fdcaeb61714402a7bc1c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 6 Sep 2022 09:48:12 +0200 Subject: [PATCH 14/43] added complete test suite for helper wrapper --- Cargo.lock | 12 +- fsrc-core/src/pus/verification.rs | 460 ++++++++++++++++++++---------- 2 files changed, 310 insertions(+), 162 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c38b37..28701b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -618,18 +618,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57" +checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09" +checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487" dependencies = [ "proc-macro2", "quote", diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 9afc557..776aa0d 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -218,7 +218,7 @@ impl VerificationReporter { } pub fn acceptance_failure( - mut self, + &mut self, token: VerificationToken, sender: &mut (impl VerificationSender + ?Sized), params: FailParams, @@ -488,7 +488,7 @@ impl VerificationReporterWithSender { } pub fn acceptance_failure( - mut self, + &mut self, token: VerificationToken, params: FailParams, ) -> Result<(), VerificationErrorWithToken> { @@ -620,7 +620,7 @@ mod tests { use alloc::vec::Vec; use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusPacket}; use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; - use spacepackets::time::{CdsShortTimeProvider, TimeWriter}; + use spacepackets::time::CdsShortTimeProvider; use spacepackets::tm::{PusTm, PusTmSecondaryHeaderT}; use spacepackets::{CcsdsPacket, SpHeader}; use std::collections::VecDeque; @@ -645,27 +645,31 @@ mod tests { } struct TestBase<'a> { - #[allow(dead_code)] - ts: CdsShortTimeProvider, vr: VerificationReporter, #[allow(dead_code)] tc: PusTc<'a>, - req_id: RequestId, } + impl<'a> TestBase<'a> { + fn rep(&mut self) -> &mut VerificationReporter { + &mut self.vr + } + } struct TestBaseWithHelper<'a, E> { - #[allow(dead_code)] - ts: CdsShortTimeProvider, helper: VerificationReporterWithSender, #[allow(dead_code)] tc: PusTc<'a>, - req_id: RequestId, } - fn base_reporter_and_stamper() -> (CdsShortTimeProvider, VerificationReporter) { - let time_stamper = CdsShortTimeProvider::default(); - let cfg = VerificationReporterCfg::new(time_stamper, 0x02); - (time_stamper, VerificationReporter::new(cfg)) + impl<'a, E> TestBaseWithHelper<'a, E> { + fn rep(&mut self) -> &mut VerificationReporter { + &mut self.helper.reporter + } + } + + fn base_reporter() -> VerificationReporter { + let cfg = VerificationReporterCfg::new(CdsShortTimeProvider::default(), TEST_APID); + VerificationReporter::new(cfg) } fn base_tc_init(app_data: Option<&[u8]>) -> (PusTc, RequestId) { @@ -676,39 +680,28 @@ mod tests { (pus_tc, req_id) } - fn base_init() -> (TestBase<'static>, VerificationToken) { - let (ts, mut reporter) = base_reporter_and_stamper(); + fn base_init(api_sel: bool) -> (TestBase<'static>, VerificationToken) { + let mut reporter = base_reporter(); let (tc, req_id) = base_tc_init(None); - let init_tok = reporter.add_tc(&tc); - ( - TestBase { - ts, - vr: reporter, - tc, - req_id, - }, - init_tok, - ) + let init_tok; + if api_sel { + init_tok = reporter.add_tc_with_req_id(req_id); + } else { + init_tok = reporter.add_tc(&tc); + } + (TestBase { vr: reporter, tc }, init_tok) } fn base_with_helper_init() -> ( TestBaseWithHelper<'static, ()>, VerificationToken, ) { - let (ts, mut reporter) = base_reporter_and_stamper(); - let (tc, req_id) = base_tc_init(None); + let mut reporter = base_reporter(); + let (tc, _) = base_tc_init(None); let init_tok = reporter.add_tc(&tc); let sender = TestSender::default(); let helper = VerificationReporterWithSender::new_from_reporter(reporter, Box::new(sender)); - ( - TestBaseWithHelper { - ts, - helper: helper, - tc, - req_id, - }, - init_tok, - ) + (TestBaseWithHelper { helper, tc }, init_tok) } impl VerificationSender<()> for TestSender { @@ -756,11 +749,11 @@ mod tests { #[test] fn test_basic_acceptance_success() { - let (mut b, tok) = base_init(); + let (mut b, tok) = base_init(false); let mut sender = TestSender::default(); b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) .expect("Sending acceptance success failed"); - acceptance_check(&mut sender, &b.req_id); + acceptance_check(&mut sender, &tok.req_id); } #[test] @@ -770,23 +763,10 @@ mod tests { .acceptance_success(tok, &EMPTY_STAMP) .expect("Sending acceptance success failed"); let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); - acceptance_check(sender, &b.req_id); + acceptance_check(sender, &tok.req_id); } - #[test] - pub fn test_basic_acceptance_failure() { - let (time_stamper, mut reporter) = base_reporter_and_stamper(); - let (tc, req_id) = base_tc_init(None); - reporter.dest_id = 5; - let verif_token = reporter.add_tc(&tc); - let mut stamp_buf = [1, 2, 3, 4, 5, 6, 7]; - time_stamper.write_to_bytes(&mut stamp_buf).unwrap(); - let mut sender = TestSender::default(); - let fail_code = EcssEnumU16::new(2); - let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, None); - reporter - .acceptance_failure(verif_token, &mut sender, fail_params) - .expect("Sending acceptance success failed"); + fn acceptance_fail_check(sender: &mut TestSender, req_id: RequestId, stamp_buf: [u8; 7]) { let cmp_info = TmInfo { time_stamp: stamp_buf, subservice: 2, @@ -802,18 +782,42 @@ mod tests { } #[test] - pub fn test_basic_acceptance_failure_with_fail_data() { - let (_, mut reporter) = base_reporter_and_stamper(); - let (tc, req_id) = base_tc_init(None); - let verif_token = reporter.add_tc(&tc); + fn test_basic_acceptance_failure() { + let (mut b, tok) = base_init(true); + b.rep().dest_id = 5; + let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; + let mut sender = TestSender::default(); + let fail_code = EcssEnumU16::new(2); + let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, None); + b.vr.acceptance_failure(tok, &mut sender, fail_params) + .expect("Sending acceptance success failed"); + acceptance_fail_check(&mut sender, tok.req_id, stamp_buf); + } + + #[test] + fn test_basic_acceptance_failure_with_helper() { + let (mut b, tok) = base_with_helper_init(); + b.rep().dest_id = 5; + let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; + let fail_code = EcssEnumU16::new(2); + let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, None); + b.helper + .acceptance_failure(tok, fail_params) + .expect("Sending acceptance success failed"); + let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); + acceptance_fail_check(sender, tok.req_id, stamp_buf); + } + + #[test] + fn test_basic_acceptance_failure_with_fail_data() { + let (mut b, tok) = base_init(false); let mut sender = TestSender::default(); let fail_code = EcssEnumU8::new(10); let fail_data = EcssEnumU32::new(12); let mut fail_data_raw = [0; 4]; fail_data.to_bytes(&mut fail_data_raw).unwrap(); let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); - reporter - .acceptance_failure(verif_token, &mut sender, fail_params) + b.vr.acceptance_failure(tok, &mut sender, fail_params) .expect("Sending acceptance success failed"); let cmp_info = TmInfo { time_stamp: EMPTY_STAMP, @@ -822,32 +826,14 @@ mod tests { apid: TEST_APID, msg_counter: 0, additional_data: Some([10, 0, 0, 0, 12].to_vec()), - req_id, + req_id: tok.req_id, }; assert_eq!(sender.service_queue.len(), 1); let info = sender.service_queue.pop_front().unwrap(); assert_eq!(info, cmp_info); } - #[test] - fn test_start_failure() { - let (_, mut reporter) = base_reporter_and_stamper(); - let (tc, req_id) = base_tc_init(None); - let verif_token = reporter.add_tc(&tc); - let mut sender = TestSender::default(); - let fail_code = EcssEnumU8::new(22); - let fail_data: i32 = -12; - let mut fail_data_raw = [0; 4]; - fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); - let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); - - let accepted_token = reporter - .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let empty = reporter - .start_failure(accepted_token, &mut sender, fail_params) - .expect("Start failure failure"); - assert_eq!(empty, ()); + fn start_fail_check(sender: &mut TestSender, req_id: RequestId, fail_data_raw: [u8; 4]) { assert_eq!(sender.service_queue.len(), 2); let mut cmp_info = TmInfo { time_stamp: EMPTY_STAMP, @@ -875,35 +861,48 @@ mod tests { } #[test] - fn test_steps_success() { - let (_, mut reporter) = base_reporter_and_stamper(); - let (tc, req_id) = base_tc_init(None); - let verif_token = reporter.add_tc(&tc); + fn test_start_failure() { + let (mut b, tok) = base_init(false); let mut sender = TestSender::default(); - let accepted_token = reporter - .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) + let fail_code = EcssEnumU8::new(22); + let fail_data: i32 = -12; + let mut fail_data_raw = [0; 4]; + fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); + let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); + + let accepted_token = + b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let empty = + b.vr.start_failure(accepted_token, &mut sender, fail_params) + .expect("Start failure failure"); + assert_eq!(empty, ()); + start_fail_check(&mut sender, tok.req_id, fail_data_raw); + } + + #[test] + fn test_start_failure_with_helper() { + let (mut b, tok) = base_with_helper_init(); + let fail_code = EcssEnumU8::new(22); + let fail_data: i32 = -12; + let mut fail_data_raw = [0; 4]; + fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); + let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); + + let accepted_token = b + .helper + .acceptance_success(tok, &EMPTY_STAMP) .expect("Sending acceptance success failed"); - let started_token = reporter - .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let mut empty = reporter - .step_success( - &started_token, - &mut sender, - &EMPTY_STAMP, - EcssEnumU8::new(0), - ) - .expect("Sending step 0 success failed"); - assert_eq!(empty, ()); - empty = reporter - .step_success( - &started_token, - &mut sender, - &EMPTY_STAMP, - EcssEnumU8::new(1), - ) - .expect("Sending step 1 success failed"); + let empty = b + .helper + .start_failure(accepted_token, fail_params) + .expect("Start failure failure"); assert_eq!(empty, ()); + let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); + start_fail_check(sender, tok.req_id, fail_data_raw); + } + + fn step_success_check(sender: &mut TestSender, req_id: RequestId) { let mut cmp_info = TmInfo { time_stamp: EMPTY_STAMP, subservice: 1, @@ -951,44 +950,68 @@ mod tests { } #[test] - fn test_step_failure() { - let (_, mut reporter) = base_reporter_and_stamper(); - let (tc, req_id) = base_tc_init(None); - let verif_token = reporter.add_tc(&tc); + fn test_steps_success() { + let (mut b, tok) = base_init(false); let mut sender = TestSender::default(); - let fail_code = EcssEnumU32::new(0x1020); - let fail_data: f32 = -22.3232; - let mut fail_data_raw = [0; 4]; - fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); - let fail_step = EcssEnumU8::new(1); - let fail_params = FailParamsWithStep::new( - &EMPTY_STAMP, - &fail_step, - &fail_code, - Some(fail_data_raw.as_slice()), - ); - - let accepted_token = reporter - .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) + let accepted_token = b + .rep() + .acceptance_success(tok, &mut sender, &EMPTY_STAMP) .expect("Sending acceptance success failed"); - let started_token = reporter + let started_token = b + .rep() .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) .expect("Sending start success failed"); - let mut empty = reporter + let mut empty = b + .rep() .step_success( &started_token, &mut sender, &EMPTY_STAMP, EcssEnumU8::new(0), ) - .expect("Sending completion success failed"); + .expect("Sending step 0 success failed"); assert_eq!(empty, ()); - empty = reporter - .step_failure(started_token, &mut sender, fail_params) - .expect("Step failure failed"); + empty = + b.vr.step_success( + &started_token, + &mut sender, + &EMPTY_STAMP, + EcssEnumU8::new(1), + ) + .expect("Sending step 1 success failed"); assert_eq!(empty, ()); assert_eq!(sender.service_queue.len(), 4); + step_success_check(&mut sender, tok.req_id); + } + #[test] + fn test_steps_success_with_helper() { + let (mut b, tok) = base_with_helper_init(); + let accepted_token = b + .helper + .acceptance_success(tok, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = b + .helper + .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let mut empty = b + .helper + .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0)) + .expect("Sending step 0 success failed"); + assert_eq!(empty, ()); + empty = b + .helper + .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(1)) + .expect("Sending step 1 success failed"); + assert_eq!(empty, ()); + let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); + assert_eq!(sender.service_queue.len(), 4); + step_success_check(sender, tok.req_id); + } + + fn check_step_failure(sender: &mut TestSender, req_id: RequestId, fail_data_raw: [u8; 4]) { + assert_eq!(sender.service_queue.len(), 4); let mut cmp_info = TmInfo { time_stamp: EMPTY_STAMP, subservice: 1, @@ -1047,25 +1070,83 @@ mod tests { } #[test] - fn test_completion_failure() { - let (_, mut reporter) = base_reporter_and_stamper(); - let (tc, req_id) = base_tc_init(None); - let verif_token = reporter.add_tc(&tc); + fn test_step_failure() { + let (mut b, tok) = base_init(false); let mut sender = TestSender::default(); + let req_id = tok.req_id; let fail_code = EcssEnumU32::new(0x1020); - let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, None); + let fail_data: f32 = -22.3232; + let mut fail_data_raw = [0; 4]; + fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); + let fail_step = EcssEnumU8::new(1); + let fail_params = FailParamsWithStep::new( + &EMPTY_STAMP, + &fail_step, + &fail_code, + Some(fail_data_raw.as_slice()), + ); - let accepted_token = reporter - .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = reporter - .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let empty = reporter - .completion_failure(started_token, &mut sender, fail_params) - .expect("Completion failure"); + let accepted_token = + b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = + b.vr.start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let mut empty = + b.vr.step_success( + &started_token, + &mut sender, + &EMPTY_STAMP, + EcssEnumU8::new(0), + ) + .expect("Sending completion success failed"); assert_eq!(empty, ()); + empty = + b.vr.step_failure(started_token, &mut sender, fail_params) + .expect("Step failure failed"); + assert_eq!(empty, ()); + check_step_failure(&mut sender, req_id, fail_data_raw); + } + #[test] + fn test_steps_failure_with_helper() { + let (mut b, tok) = base_with_helper_init(); + let req_id = tok.req_id; + let fail_code = EcssEnumU32::new(0x1020); + let fail_data: f32 = -22.3232; + let mut fail_data_raw = [0; 4]; + fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); + let fail_step = EcssEnumU8::new(1); + let fail_params = FailParamsWithStep::new( + &EMPTY_STAMP, + &fail_step, + &fail_code, + Some(fail_data_raw.as_slice()), + ); + + let accepted_token = b + .helper + .acceptance_success(tok, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = b + .helper + .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let mut empty = b + .helper + .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0)) + .expect("Sending completion success failed"); + assert_eq!(empty, ()); + empty = b + .helper + .step_failure(started_token, fail_params) + .expect("Step failure failed"); + assert_eq!(empty, ()); + let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); + check_step_failure(sender, req_id, fail_data_raw); + } + + fn completion_fail_check(sender: &mut TestSender, req_id: RequestId) { assert_eq!(sender.service_queue.len(), 3); let mut cmp_info = TmInfo { @@ -1106,21 +1187,51 @@ mod tests { } #[test] - fn test_complete_success_sequence() { - let (_, mut reporter) = base_reporter_and_stamper(); - let (tc, req_id) = base_tc_init(None); - let verif_token = reporter.add_tc(&tc); + fn test_completion_failure() { + let (mut b, tok) = base_init(false); let mut sender = TestSender::default(); - let accepted_token = reporter - .acceptance_success(verif_token, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = reporter - .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let empty = reporter - .completion_success(started_token, &mut sender, &EMPTY_STAMP) - .expect("Sending completion success failed"); + let req_id = tok.req_id; + let fail_code = EcssEnumU32::new(0x1020); + let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, None); + + let accepted_token = + b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = + b.vr.start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let empty = + b.vr.completion_failure(started_token, &mut sender, fail_params) + .expect("Completion failure"); assert_eq!(empty, ()); + completion_fail_check(&mut sender, req_id); + } + + #[test] + fn test_completion_failure_with_helper() { + let (mut b, tok) = base_with_helper_init(); + let req_id = tok.req_id; + let fail_code = EcssEnumU32::new(0x1020); + let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, None); + + let accepted_token = b + .helper + .acceptance_success(tok, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = b + .helper + .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let empty = b + .helper + .completion_failure(started_token, fail_params) + .expect("Completion failure"); + assert_eq!(empty, ()); + let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); + completion_fail_check(sender, req_id); + } + + fn completion_success_check(sender: &mut TestSender, req_id: RequestId) { assert_eq!(sender.service_queue.len(), 3); let cmp_info = TmInfo { time_stamp: EMPTY_STAMP, @@ -1157,4 +1268,41 @@ mod tests { info = sender.service_queue.pop_front().unwrap(); assert_eq!(info, cmp_info); } + + #[test] + fn test_complete_success_sequence() { + let (mut b, tok) = base_init(false); + let mut sender = TestSender::default(); + let accepted_token = + b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = + b.vr.start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let empty = + b.vr.completion_success(started_token, &mut sender, &EMPTY_STAMP) + .expect("Sending completion success failed"); + assert_eq!(empty, ()); + completion_success_check(&mut sender, tok.req_id); + } + + #[test] + fn test_complete_success_sequence_with_helper() { + let (mut b, tok) = base_with_helper_init(); + let accepted_token = b + .helper + .acceptance_success(tok, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = b + .helper + .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) + .expect("Sending start success failed"); + let empty = b + .helper + .completion_success(started_token, &EMPTY_STAMP) + .expect("Sending completion success failed"); + assert_eq!(empty, ()); + let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); + completion_success_check(sender, tok.req_id); + } } From d917703e7d5b13a6e08a6801b34e41fb0af00fb1 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 6 Sep 2022 10:14:47 +0200 Subject: [PATCH 15/43] added some failure tests --- fsrc-core/src/pus/verification.rs | 135 ++++++++++++++++++++++-------- spacepackets | 2 +- 2 files changed, 102 insertions(+), 35 deletions(-) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 776aa0d..58fb382 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -63,7 +63,7 @@ impl RequestId { #[derive(Debug, Clone)] pub enum VerificationError { SendError(E), - TimeStampError(TimestampError), + TimestampError(TimestampError), ByteConversionError(ByteConversionError), PusError(PusError), } @@ -77,17 +77,17 @@ pub trait VerificationSender: Downcast { impl_downcast!(VerificationSender); -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct VerificationToken { state: PhantomData, req_id: RequestId, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct StateNone; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct StateAccepted; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct StateStarted; impl VerificationToken { @@ -184,6 +184,10 @@ impl VerificationReporter { } } + pub fn allowed_source_data_len(&self) -> usize { + self.source_data_buf.capacity() + } + pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken { self.add_tc_with_req_id(RequestId::new(pus_tc)) } @@ -617,12 +621,13 @@ mod tests { VerificationSender, VerificationToken, }; use alloc::boxed::Box; + use alloc::format; use alloc::vec::Vec; use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusPacket}; use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; use spacepackets::time::CdsShortTimeProvider; use spacepackets::tm::{PusTm, PusTmSecondaryHeaderT}; - use spacepackets::{CcsdsPacket, SpHeader}; + use spacepackets::{ByteConversionError, CcsdsPacket, SpHeader}; use std::collections::VecDeque; const TEST_APID: u16 = 0x02; @@ -644,6 +649,45 @@ mod tests { pub service_queue: VecDeque, } + impl VerificationSender<()> for TestSender { + fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError<()>> { + assert_eq!(PusPacket::service(&tm), 1); + assert!(tm.source_data().is_some()); + let mut time_stamp = [0; 7]; + time_stamp.clone_from_slice(&tm.time_stamp()[0..7]); + let src_data = tm.source_data().unwrap(); + assert!(src_data.len() >= 4); + let req_id = RequestId::from_bytes(&src_data[0..RequestId::SIZE_AS_BYTES]).unwrap(); + let mut vec = None; + if src_data.len() > 4 { + let mut new_vec = Vec::new(); + new_vec.extend_from_slice(&src_data[RequestId::SIZE_AS_BYTES..]); + vec = Some(new_vec); + } + self.service_queue.push_back(TmInfo { + subservice: PusPacket::subservice(&tm), + apid: tm.apid(), + msg_counter: tm.msg_counter(), + dest_id: tm.dest_id(), + time_stamp, + req_id, + additional_data: vec, + }); + Ok(()) + } + } + + #[derive(Debug, Copy, Clone, Eq, PartialEq)] + struct DummyError {} + #[derive(Default)] + struct FallibleSender {} + + impl VerificationSender for FallibleSender { + fn send_verification_tm(&mut self, _: PusTm) -> Result<(), VerificationError> { + Err(VerificationError::SendError(DummyError {})) + } + } + struct TestBase<'a> { vr: VerificationReporter, #[allow(dead_code)] @@ -704,34 +748,6 @@ mod tests { (TestBaseWithHelper { helper, tc }, init_tok) } - impl VerificationSender<()> for TestSender { - fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError<()>> { - assert_eq!(PusPacket::service(&tm), 1); - assert!(tm.source_data().is_some()); - let mut time_stamp = [0; 7]; - time_stamp.clone_from_slice(tm.time_stamp()); - let src_data = tm.source_data().unwrap(); - assert!(src_data.len() >= 4); - let req_id = RequestId::from_bytes(&src_data[0..RequestId::SIZE_AS_BYTES]).unwrap(); - let mut vec = None; - if src_data.len() > 4 { - let mut new_vec = Vec::new(); - new_vec.extend_from_slice(&src_data[RequestId::SIZE_AS_BYTES..]); - vec = Some(new_vec); - } - self.service_queue.push_back(TmInfo { - subservice: PusPacket::subservice(&tm), - apid: tm.apid(), - msg_counter: tm.msg_counter(), - dest_id: tm.dest_id(), - time_stamp, - req_id, - additional_data: vec, - }); - Ok(()) - } - } - fn acceptance_check(sender: &mut TestSender, req_id: &RequestId) { let cmp_info = TmInfo { time_stamp: EMPTY_STAMP, @@ -766,6 +782,23 @@ mod tests { acceptance_check(sender, &tok.req_id); } + #[test] + fn test_acceptance_send_fails() { + let (mut b, tok) = base_init(false); + let mut faulty_sender = FallibleSender::default(); + let res = + b.vr.acceptance_success(tok, &mut faulty_sender, &EMPTY_STAMP); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.1, tok); + match err.0 { + VerificationError::SendError(e) => { + assert_eq!(e, DummyError {}) + } + _ => panic!("{}", format!("Unexpected error {:?}", err.0)), + } + } + fn acceptance_fail_check(sender: &mut TestSender, req_id: RequestId, stamp_buf: [u8; 7]) { let cmp_info = TmInfo { time_stamp: stamp_buf, @@ -808,6 +841,40 @@ mod tests { acceptance_fail_check(sender, tok.req_id, stamp_buf); } + #[test] + fn test_acceptance_fail_data_too_large() { + let (mut b, tok) = base_with_helper_init(); + b.rep().dest_id = 5; + let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; + let fail_code = EcssEnumU16::new(2); + let fail_data: [u8; 16] = [0; 16]; + // 4 req ID + 1 byte step + 2 byte error code + 8 byte fail data + assert_eq!(b.rep().allowed_source_data_len(), 15); + let fail_params = + FailParams::new(stamp_buf.as_slice(), &fail_code, Some(fail_data.as_slice())); + let res = b.helper.acceptance_failure(tok, fail_params); + assert!(res.is_err()); + let err_with_token = res.unwrap_err(); + assert_eq!(err_with_token.1, tok); + match err_with_token.0 { + VerificationError::ByteConversionError(e) => match e { + ByteConversionError::ToSliceTooSmall(missmatch) => { + assert_eq!( + missmatch.expected, + fail_data.len() + RequestId::SIZE_AS_BYTES + fail_code.byte_width() + ); + assert_eq!(missmatch.found, b.rep().allowed_source_data_len()); + } + _ => { + panic!("{}", format!("Unexpected error {:?}", e)) + } + }, + _ => { + panic!("{}", format!("Unexpected error {:?}", err_with_token.0)) + } + } + } + #[test] fn test_basic_acceptance_failure_with_fail_data() { let (mut b, tok) = base_init(false); diff --git a/spacepackets b/spacepackets index c72c5ad..94489da 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit c72c5ad4aaca277754e94a16377a7d4a6eff5d8e +Subproject commit 94489da00323dc6caf24e05e240c80fc10b5d8cc From 5a5985dfb60301f1a05cd9e44b6d11897f79c5ac Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 6 Sep 2022 10:44:13 +0200 Subject: [PATCH 16/43] basic docs --- fsrc-core/src/pus/mod.rs | 5 ++ fsrc-core/src/pus/verification.rs | 94 +++++++++++++++++++++++++---- fsrc-core/src/tmtc/ccsds_distrib.rs | 2 +- 3 files changed, 87 insertions(+), 14 deletions(-) diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs index 6abb03a..f8f4901 100644 --- a/fsrc-core/src/pus/mod.rs +++ b/fsrc-core/src/pus/mod.rs @@ -1,2 +1,7 @@ +//! All PUS support modules +//! +//! Currenty includes: +//! +//! 1. PUS Verification Service 1 module inside [verification] #[cfg(feature = "alloc")] pub mod verification; diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 58fb382..6fe87a6 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -1,3 +1,10 @@ +//! # PUS Verification Service 1 Module +//! +//! This module allows packaging and sending PUS Service 1 packets. It is conforming to section +//! 8 of the PUS standard ECSS-E-ST-70-41C. +//! +//! # Example +//! TODO: Cross Ref integration test which will be provided use crate::pool::{LocalPool, StoreAddr, StoreError}; use alloc::boxed::Box; use alloc::sync::Arc; @@ -9,7 +16,7 @@ use delegate::delegate; use downcast_rs::{impl_downcast, Downcast}; use spacepackets::ecss::{EcssEnumeration, PusError}; use spacepackets::tc::PusTc; -use spacepackets::time::{CcsdsTimeProvider, TimestampError}; +use spacepackets::time::TimestampError; use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; @@ -21,6 +28,8 @@ use std::sync::MutexGuard; #[cfg(feature = "std")] use std::sync::{mpsc, Mutex}; +/// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard +/// This field equivalent to the first two bytes of the CCSDS space packet header. #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub struct RequestId { version_number: u8, @@ -51,6 +60,7 @@ impl RequestId { } } impl RequestId { + /// This allows extracting the request ID from a given PUS telecommand. pub fn new(tc: &PusTc) -> Self { RequestId { version_number: tc.ccsds_version(), @@ -60,23 +70,36 @@ impl RequestId { } } +/// Generic error type which is also able to wrap a user send error with the user supplied type E. #[derive(Debug, Clone)] pub enum VerificationError { + /// Errors related to sending the verification telemetry to a TM recipient SendError(E), + /// Errors related to the time stamp format of the telemetry TimestampError(TimestampError), + /// Errors related to byte conversion, for example unsufficient buffer size for given data ByteConversionError(ByteConversionError), + /// Errors related to PUS packet format PusError(PusError), } +/// If a verification operation fails, the passed token will be returned as well. This allows +/// re-trying the operation at a later point. #[derive(Debug, Clone)] pub struct VerificationErrorWithToken(VerificationError, VerificationToken); +/// Generic trait for a user supplied sender object. This sender object is responsible for sending +/// PUS Service 1 Verification Telemetry to a verification TM recipient. The [Downcast] trait +/// is implemented to allow passing the sender as a boxed trait object and still retrieve the +/// concrete type at a later point. pub trait VerificationSender: Downcast { fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError>; } impl_downcast!(VerificationSender); +/// Support token to allow type-state programming. This prevents calling the verification +/// steps in an invalid order. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct VerificationToken { state: PhantomData, @@ -102,26 +125,39 @@ impl VerificationToken { pub struct VerificationReporterCfg { pub apid: u16, pub dest_id: u16, - pub step_field_width: u8, - pub failure_code_field_width: u8, + pub step_field_width: usize, + pub fail_code_field_width: usize, pub max_fail_data_len: usize, - pub max_stamp_len: usize, } impl VerificationReporterCfg { - pub fn new(time_stamper: impl CcsdsTimeProvider, apid: u16) -> Self { - let max_stamp_len = time_stamper.len_as_bytes(); + /// Create a new configuration for the verification reporter. This includes following parameters: + /// + /// 1. Destination ID and APID, which could remain constant after construction. These parameters + /// can be tweaked in the reporter after construction. + /// 2. Maximum expected field sizes. The parameters of this configuration struct will be used + /// to determine required maximum buffer sizes and there will be no addition allocation or + /// configurable buffer parameters after [VerificationReporter] construction. + /// + /// This means the user has supply the maximum expected field sizes of verification messages + /// before constructing the reporter. + pub fn new( + apid: u16, + step_field_width: usize, + fail_code_field_width: usize, + max_fail_data_len: usize, + ) -> Self { Self { apid, dest_id: 0, - step_field_width: size_of::() as u8, - failure_code_field_width: size_of::() as u8, - max_fail_data_len: 2 * size_of::(), - max_stamp_len, + step_field_width, + fail_code_field_width, + max_fail_data_len, } } } +/// Composite helper struct to pass failure parameters to the [VerificationReporter] pub struct FailParams<'a> { time_stamp: &'a [u8], failure_code: &'a dyn EcssEnumeration, @@ -142,6 +178,7 @@ impl<'a> FailParams<'a> { } } +/// Composite helper struct to pass step failure parameters to the [VerificationReporter] pub struct FailParamsWithStep<'a> { bp: FailParams<'a>, step: &'a dyn EcssEnumeration, @@ -161,6 +198,8 @@ impl<'a> FailParamsWithStep<'a> { } } +/// Primary verification handler. It provides an API to send PUS 1 verification telemetry packets +/// and verify the various steps of telecommand handling as specified in the PUS standard. pub struct VerificationReporter { pub apid: u16, pub dest_id: u16, @@ -178,7 +217,7 @@ impl VerificationReporter { 0; RequestId::SIZE_AS_BYTES + cfg.step_field_width as usize - + cfg.failure_code_field_width as usize + + cfg.fail_code_field_width as usize + cfg.max_fail_data_len ], } @@ -188,14 +227,19 @@ impl VerificationReporter { self.source_data_buf.capacity() } + /// Initialize verification handling by passing a TC reference. This returns a token required + /// to call the acceptance functions pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken { self.add_tc_with_req_id(RequestId::new(pus_tc)) } + /// Same as [Self::add_tc] but pass a request ID instead of the direct telecommand. + /// This can be useful if the executing thread does not have full access to the telecommand. pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken { VerificationToken::::new(req_id) } + /// Package and send a PUS TM\[1, 1\] packet, see 8.1.2.1 of the PUS standard pub fn acceptance_success( &mut self, token: VerificationToken, @@ -221,6 +265,7 @@ impl VerificationReporter { }) } + /// Package and send a PUS TM\[1, 2\] packet, see 8.1.2.2 of the PUS standard pub fn acceptance_failure( &mut self, token: VerificationToken, @@ -237,6 +282,9 @@ impl VerificationReporter { Ok(()) } + /// Package and send a PUS TM\[1, 3\] packet, see 8.1.2.3 of the PUS standard. + /// + /// Requires a token previously acquired by calling [Self::acceptance_success]. pub fn start_success( &mut self, token: VerificationToken, @@ -262,6 +310,10 @@ impl VerificationReporter { }) } + /// Package and send a PUS TM\[1, 4\] packet, see 8.1.2.4 of the PUS standard. + /// + /// Requires a token previously acquired by calling [Self::acceptance_success]. It consumes + /// the token because verification handling is done. pub fn start_failure( &mut self, token: VerificationToken, @@ -278,6 +330,9 @@ impl VerificationReporter { Ok(()) } + /// Package and send a PUS TM\[1, 5\] packet, see 8.1.2.5 of the PUS standard. + /// + /// Requires a token previously acquired by calling [Self::start_success]. pub fn step_success( &mut self, token: &VerificationToken, @@ -291,6 +346,10 @@ impl VerificationReporter { Ok(()) } + /// Package and send a PUS TM\[1, 6\] packet, see 8.1.2.6 of the PUS standard. + /// + /// Requires a token previously acquired by calling [Self::start_success]. It consumes the + /// token because verification handling is done. pub fn step_failure( &mut self, token: VerificationToken, @@ -307,6 +366,10 @@ impl VerificationReporter { Ok(()) } + /// Package and send a PUS TM\[1, 7\] packet, see 8.1.2.7 of the PUS standard. + /// + /// Requires a token previously acquired by calling [Self::start_success]. It consumes the + /// token because verification handling is done. pub fn completion_success( &mut self, token: VerificationToken, @@ -329,6 +392,10 @@ impl VerificationReporter { Ok(()) } + /// Package and send a PUS TM\[1, 8\] packet, see 8.1.2.8 of the PUS standard. + /// + /// Requires a token previously acquired by calling [Self::start_success]. It consumes the + /// token because verification handling is done. pub fn completion_failure( &mut self, token: VerificationToken, @@ -458,6 +525,8 @@ impl VerificationReporter { } } +/// Helper object which caches the sender passed as a trait object. Provides the same +/// API as [VerificationReporter] but without the explicit sender arguments. pub struct VerificationReporterWithSender { reporter: VerificationReporter, pub sender: Box>, @@ -625,7 +694,6 @@ mod tests { use alloc::vec::Vec; use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusPacket}; use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; - use spacepackets::time::CdsShortTimeProvider; use spacepackets::tm::{PusTm, PusTmSecondaryHeaderT}; use spacepackets::{ByteConversionError, CcsdsPacket, SpHeader}; use std::collections::VecDeque; @@ -712,7 +780,7 @@ mod tests { } fn base_reporter() -> VerificationReporter { - let cfg = VerificationReporterCfg::new(CdsShortTimeProvider::default(), TEST_APID); + let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8); VerificationReporter::new(cfg) } diff --git a/fsrc-core/src/tmtc/ccsds_distrib.rs b/fsrc-core/src/tmtc/ccsds_distrib.rs index ec084a6..92d6c39 100644 --- a/fsrc-core/src/tmtc/ccsds_distrib.rs +++ b/fsrc-core/src/tmtc/ccsds_distrib.rs @@ -8,7 +8,7 @@ //! pass raw or CCSDS packets to it. Upon receiving a packet, it performs the following steps: //! //! 1. It tries to identify the target Application Process Identifier (APID) based on the -//! respective CCSDS space packet header field. If that process fails, a [PacketError] is +//! respective CCSDS space packet header field. If that process fails, a [ByteConversionError] is //! returned to the user //! 2. If a valid APID is found and matches one of the APIDs provided by //! [CcsdsPacketHandler::valid_apids], it will pass the packet to the user provided From fc77300ee27981dcf2edffc64d64d900622998e3 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 6 Sep 2022 10:45:02 +0200 Subject: [PATCH 17/43] some more docs --- fsrc-core/src/pus/verification.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 6fe87a6..0066213 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -3,6 +3,10 @@ //! This module allows packaging and sending PUS Service 1 packets. It is conforming to section //! 8 of the PUS standard ECSS-E-ST-70-41C. //! +//! The core object to report TC verification progress is the [VerifictionReporter]. It exposes +//! an API which uses type-state programming to avoid calling the verification steps in +//! an invalid order. +//! //! # Example //! TODO: Cross Ref integration test which will be provided use crate::pool::{LocalPool, StoreAddr, StoreError}; From ac56fbae99adec8a74b2dba6f581b85e5b0bc815 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 6 Sep 2022 10:47:10 +0200 Subject: [PATCH 18/43] some more docs --- fsrc-core/src/pus/mod.rs | 2 +- fsrc-core/src/pus/verification.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs index f8f4901..fb3a317 100644 --- a/fsrc-core/src/pus/mod.rs +++ b/fsrc-core/src/pus/mod.rs @@ -2,6 +2,6 @@ //! //! Currenty includes: //! -//! 1. PUS Verification Service 1 module inside [verification] +//! 1. PUS Verification Service 1 module inside [verification]. Requires [alloc] support. #[cfg(feature = "alloc")] pub mod verification; diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 0066213..578986f 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -3,10 +3,10 @@ //! This module allows packaging and sending PUS Service 1 packets. It is conforming to section //! 8 of the PUS standard ECSS-E-ST-70-41C. //! -//! The core object to report TC verification progress is the [VerifictionReporter]. It exposes +//! The core object to report TC verification progress is the [VerificationReporter]. It exposes //! an API which uses type-state programming to avoid calling the verification steps in //! an invalid order. -//! +//! //! # Example //! TODO: Cross Ref integration test which will be provided use crate::pool::{LocalPool, StoreAddr, StoreError}; From df7282df25ac7adea957a7e48f66639150bcd412 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 01:06:32 +0200 Subject: [PATCH 19/43] basic shared verificator test --- fsrc-core/src/pool.rs | 2 +- fsrc-core/src/pus/verification.rs | 48 +++++++----- fsrc-core/tests/pool_test.rs | 6 +- fsrc-core/tests/verification_test.rs | 110 +++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 24 deletions(-) diff --git a/fsrc-core/src/pool.rs b/fsrc-core/src/pool.rs index af68d90..79100bb 100644 --- a/fsrc-core/src/pool.rs +++ b/fsrc-core/src/pool.rs @@ -195,7 +195,7 @@ impl LocalPool { } let addr = self.reserve(len)?; let raw_pos = self.raw_pos(&addr).unwrap(); - let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..len]; + let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + len]; Ok((addr, block)) } diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 578986f..7c3e92c 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -26,11 +26,7 @@ use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; #[cfg(feature = "std")] -use std::sync::mpsc::SendError; -#[cfg(feature = "std")] -use std::sync::MutexGuard; -#[cfg(feature = "std")] -use std::sync::{mpsc, Mutex}; +use std::sync::{mpsc, RwLock, RwLockWriteGuard}; /// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard /// This field equivalent to the first two bytes of the CCSDS space packet header. @@ -42,12 +38,16 @@ pub struct RequestId { } impl RequestId { - const SIZE_AS_BYTES: usize = size_of::(); + pub const SIZE_AS_BYTES: usize = size_of::(); + + pub fn raw(&self) -> u32 { + ((self.version_number as u32) << 29) + | ((self.packet_id.raw() as u32) << 16) + | self.psc.raw() as u32 + } pub fn to_bytes(&self, buf: &mut [u8]) { - let raw = ((self.version_number as u32) << 29) - | ((self.packet_id.raw() as u32) << 16) - | self.psc.raw() as u32; + let raw = self.raw(); buf.copy_from_slice(raw.to_be_bytes().as_slice()); } @@ -533,17 +533,20 @@ impl VerificationReporter { /// API as [VerificationReporter] but without the explicit sender arguments. pub struct VerificationReporterWithSender { reporter: VerificationReporter, - pub sender: Box>, + pub sender: Box + Send>, } impl VerificationReporterWithSender { - pub fn new(cfg: VerificationReporterCfg, sender: Box>) -> Self { + pub fn new( + cfg: VerificationReporterCfg, + sender: Box + Send>, + ) -> Self { Self::new_from_reporter(VerificationReporter::new(cfg), sender) } pub fn new_from_reporter( reporter: VerificationReporter, - sender: Box>, + sender: Box + Send>, ) -> Self { Self { reporter, sender } } @@ -632,13 +635,13 @@ impl VerificationReporterWithSender { #[cfg(feature = "std")] pub struct StdVerifSender { pub ignore_poison_error: bool, - tm_store: Arc>, + tm_store: Arc>, tx: mpsc::Sender, } #[cfg(feature = "std")] impl StdVerifSender { - pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { + pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { Self { ignore_poison_error: true, tx, @@ -647,12 +650,17 @@ impl StdVerifSender { } } +#[cfg(feature = "std")] +unsafe impl Sync for StdVerifSender {} +#[cfg(feature = "std")] +unsafe impl Send for StdVerifSender {} + #[cfg(feature = "std")] #[derive(Debug, Eq, PartialEq)] pub enum StdVerifSenderError { PoisonError, StoreError(StoreError), - SendError(SendError), + RxDisconnected(StoreAddr), } #[cfg(feature = "std")] @@ -661,17 +669,17 @@ impl VerificationSender for StdVerifSender { &mut self, tm: PusTm, ) -> Result<(), VerificationError> { - let operation = |mut mg: MutexGuard| { + let operation = |mut mg: RwLockWriteGuard| { let (addr, buf) = mg .free_element(tm.len_packed()) .map_err(|e| VerificationError::SendError(StdVerifSenderError::StoreError(e)))?; tm.write_to(buf).map_err(VerificationError::PusError)?; - self.tx - .send(addr) - .map_err(|e| VerificationError::SendError(StdVerifSenderError::SendError(e)))?; + self.tx.send(addr).map_err(|_| { + VerificationError::SendError(StdVerifSenderError::RxDisconnected(addr)) + })?; Ok(()) }; - match self.tm_store.lock() { + match self.tm_store.write() { Ok(lock) => operation(lock), Err(poison_error) => { if self.ignore_poison_error { diff --git a/fsrc-core/tests/pool_test.rs b/fsrc-core/tests/pool_test.rs index f06f302..e116130 100644 --- a/fsrc-core/tests/pool_test.rs +++ b/fsrc-core/tests/pool_test.rs @@ -10,11 +10,11 @@ const DUMMY_DATA: [u8; 4] = [0, 1, 2, 3]; #[test] fn threaded_usage() { let pool_cfg = PoolCfg::new(vec![(16, 6), (32, 3), (8, 12)]); - let shared_dummy = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); - let shared_clone = shared_dummy.clone(); + let shared_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); + let shared_clone = shared_pool.clone(); let (tx, rx): (Sender, Receiver) = mpsc::channel(); let jh0 = thread::spawn(move || { - let mut dummy = shared_dummy.write().unwrap(); + let mut dummy = shared_pool.write().unwrap(); let addr = dummy.add(&DUMMY_DATA).expect("Writing data failed"); tx.send(addr).expect("Sending store address failed"); }); diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index 8b13789..f80efb8 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -1 +1,111 @@ +use fsrc_core::pool::{LocalPool, PoolCfg}; +use fsrc_core::pus::verification::{ + FailParams, RequestId, StdVerifSender, VerificationReporter, VerificationReporterCfg, +}; +use spacepackets::ecss::{EcssEnumU16, EcssEnumU8, PusPacket}; +use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; +use spacepackets::tm::PusTm; +use spacepackets::SpHeader; +use std::sync::atomic::{AtomicU16, Ordering}; +use std::sync::{mpsc, Arc, Mutex, RwLock}; +use std::thread; +const TEST_APID: u16 = 0x03; +const FIXED_STAMP: [u8; 7] = [0; 7]; +const PACKETS_SENT: u8 = 8; + +#[test] +fn test_shared_reporter() { + let seq_counter_0 = Arc::new(AtomicU16::new(0)); + let seq_counter_1 = seq_counter_0.clone(); + let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8); + // Shared pool object to store the verification PUS telemetry + let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]); + let shared_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); + let (tx, rx) = mpsc::channel(); + let mut sender_0 = StdVerifSender::new(shared_pool.clone(), tx.clone()); + let mut sender_1 = StdVerifSender::new(shared_pool.clone(), tx); + let reporter_0 = Arc::new(Mutex::new(VerificationReporter::new(cfg))); + let reporter_1 = reporter_0.clone(); + + let verif_sender_0 = thread::spawn(move || { + let mut sph = + SpHeader::tc(TEST_APID, seq_counter_0.fetch_add(1, Ordering::SeqCst), 0).unwrap(); + let tc_header = PusTcSecondaryHeader::new_simple(17, 1); + let pus_tc = PusTc::new(&mut sph, tc_header, None, true); + let mut mg = reporter_0.lock().unwrap(); + let token = mg.add_tc(&pus_tc); + let accepted_token = mg + .acceptance_success(token, &mut sender_0, &FIXED_STAMP) + .expect("Acceptance success failed"); + let started_token = mg + .start_success(accepted_token, &mut sender_0, &FIXED_STAMP) + .expect("Start success failed"); + mg.step_success( + &started_token, + &mut sender_0, + &FIXED_STAMP, + EcssEnumU8::new(0), + ) + .expect("Start success failed"); + mg.step_success( + &started_token, + &mut sender_0, + &FIXED_STAMP, + EcssEnumU8::new(1), + ) + .expect("Start success failed"); + mg.completion_success(started_token, &mut sender_0, &FIXED_STAMP) + .expect("Completion success failed"); + }); + + let verif_sender_1 = thread::spawn(move || { + let mut sph = + SpHeader::tc(TEST_APID, seq_counter_1.fetch_add(1, Ordering::SeqCst), 0).unwrap(); + let tc_header = PusTcSecondaryHeader::new_simple(5, 1); + let pus_tc = PusTc::new(&mut sph, tc_header, None, true); + let mut mg = reporter_1.lock().unwrap(); + let token = mg.add_tc(&pus_tc); + let accepted_token = mg + .acceptance_success(token, &mut sender_1, &FIXED_STAMP) + .expect("Acceptance success failed"); + let started_token = mg + .start_success(accepted_token, &mut sender_1, &FIXED_STAMP) + .expect("Start success failed"); + let fail_code = EcssEnumU16::new(2); + let params = FailParams::new(&FIXED_STAMP, &fail_code, None); + mg.completion_failure(started_token, &mut sender_1, params) + .expect("Completion success failed"); + }); + + let verif_receiver = thread::spawn(move || { + let mut packet_counter = 0; + let mut tm_buf: [u8; 1024] = [0; 1024]; + while packet_counter < PACKETS_SENT { + let verif_addr = rx.recv().expect("Error receiving verification packet"); + let mut rg = shared_pool.write().expect("Error locking shared pool"); + let store_guard = rg.read_with_guard(verif_addr); + let slice = store_guard.read().expect("Error reading TM slice"); + let tm_len = slice.len(); + tm_buf[0..tm_len].copy_from_slice(slice); + drop(store_guard); + drop(rg); + let (pus_tm, _) = PusTm::new_from_raw_slice(&tm_buf[0..tm_len], 7) + .expect("Error reading verification TM"); + let req_id = RequestId::from_bytes( + &pus_tm.source_data().expect("Invalid TM source data")[0..RequestId::SIZE_AS_BYTES], + ) + .unwrap(); + println!( + "Received PUS Verification TM[{},{}] for request ID {:#08x}", + pus_tm.service(), + pus_tm.subservice(), + req_id.raw() + ); + packet_counter += 1; + } + }); + verif_sender_0.join().expect("Joining thread 0 failed"); + verif_sender_1.join().expect("Joining thread 1 failed"); + verif_receiver.join().expect("Joining thread 2 failed") +} From 01c3440c0743fd24bfa2a6670e3357f1b6fd81fb Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 11:02:45 +0200 Subject: [PATCH 20/43] support cross-beam channel backend --- Cargo.lock | 1 + fsrc-core/Cargo.toml | 10 +- fsrc-core/src/pool.rs | 1 + fsrc-core/src/pus/verification.rs | 201 +++++++++++++++++++-------- fsrc-core/tests/verification_test.rs | 103 ++++++++++---- 5 files changed, 228 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28701b0..1d8bc60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,6 +223,7 @@ name = "fsrc-core" version = "0.1.0" dependencies = [ "bus", + "crossbeam-channel", "delegate", "downcast-rs", "hashbrown", diff --git a/fsrc-core/Cargo.toml b/fsrc-core/Cargo.toml index 392e4f4..160c727 100644 --- a/fsrc-core/Cargo.toml +++ b/fsrc-core/Cargo.toml @@ -22,17 +22,23 @@ default-features = false version = "2.2.3" optional = true +[dependencies.crossbeam-channel] +version= "0.5" +default-features = false + [dependencies.spacepackets] path = "../spacepackets" [dev-dependencies] -postcard = { version = "1.0.1", features = ["use-std"] } serde = "1.0.143" zerocopy = "0.6.1" once_cell = "1.13.1" +[dev-dependencies.postcard] +version = "1.0.1" + [features] default = ["std"] -std = ["downcast-rs/std", "alloc", "bus"] +std = ["downcast-rs/std", "alloc", "bus", "postcard/use-std", "crossbeam-channel/std"] alloc = [] diff --git a/fsrc-core/src/pool.rs b/fsrc-core/src/pool.rs index 79100bb..e1a00a7 100644 --- a/fsrc-core/src/pool.rs +++ b/fsrc-core/src/pool.rs @@ -87,6 +87,7 @@ type NumBlocks = u16; /// /// * `cfg`: Vector of tuples which represent a subpool. The first entry in the tuple specifies the /// number of memory blocks in the subpool, the second entry the size of the blocks +#[derive(Clone)] pub struct PoolCfg { cfg: Vec<(NumBlocks, usize)>, } diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 7c3e92c..b81a47d 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -9,11 +9,10 @@ //! //! # Example //! TODO: Cross Ref integration test which will be provided -use crate::pool::{LocalPool, StoreAddr, StoreError}; use alloc::boxed::Box; -use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; +use core::hash::{Hash, Hasher}; use core::marker::PhantomData; use core::mem::size_of; use delegate::delegate; @@ -26,17 +25,32 @@ use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; #[cfg(feature = "std")] -use std::sync::{mpsc, RwLock, RwLockWriteGuard}; +pub use stdmod::{CrossbeamVerifSender, StdVerifSender, StdVerifSenderError}; /// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard /// This field equivalent to the first two bytes of the CCSDS space packet header. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Eq, Copy, Clone)] pub struct RequestId { version_number: u8, packet_id: PacketId, psc: PacketSequenceCtrl, } +impl Hash for RequestId { + fn hash(&self, state: &mut H) { + self.raw().hash(state); + } +} + +// Implement manually to satisfy derive_hash_xor_eq lint +impl PartialEq for RequestId { + fn eq(&self, other: &Self) -> bool { + self.version_number == other.version_number + && self.packet_id == other.packet_id + && self.psc == other.psc + } +} + impl RequestId { pub const SIZE_AS_BYTES: usize = size_of::(); @@ -96,7 +110,7 @@ pub struct VerificationErrorWithToken(VerificationError, VerificationTo /// PUS Service 1 Verification Telemetry to a verification TM recipient. The [Downcast] trait /// is implemented to allow passing the sender as a boxed trait object and still retrieve the /// concrete type at a later point. -pub trait VerificationSender: Downcast { +pub trait VerificationSender: Downcast + Send { fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError>; } @@ -124,6 +138,10 @@ impl VerificationToken { req_id, } } + + pub fn req_id(&self) -> RequestId { + self.req_id + } } pub struct VerificationReporterCfg { @@ -533,20 +551,17 @@ impl VerificationReporter { /// API as [VerificationReporter] but without the explicit sender arguments. pub struct VerificationReporterWithSender { reporter: VerificationReporter, - pub sender: Box + Send>, + pub sender: Box>, } impl VerificationReporterWithSender { - pub fn new( - cfg: VerificationReporterCfg, - sender: Box + Send>, - ) -> Self { + pub fn new(cfg: VerificationReporterCfg, sender: Box>) -> Self { Self::new_from_reporter(VerificationReporter::new(cfg), sender) } pub fn new_from_reporter( reporter: VerificationReporter, - sender: Box + Send>, + sender: Box>, ) -> Self { Self { reporter, sender } } @@ -633,61 +648,127 @@ impl VerificationReporterWithSender { } #[cfg(feature = "std")] -pub struct StdVerifSender { - pub ignore_poison_error: bool, - tm_store: Arc>, - tx: mpsc::Sender, -} +mod stdmod { + use crate::pool::{LocalPool, StoreAddr, StoreError}; + use crate::pus::verification::{VerificationError, VerificationSender}; + use delegate::delegate; + use spacepackets::tm::PusTm; + use std::sync::{mpsc, Arc, RwLock, RwLockWriteGuard}; -#[cfg(feature = "std")] -impl StdVerifSender { - pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { - Self { - ignore_poison_error: true, - tx, - tm_store, + #[derive(Debug, Eq, PartialEq)] + pub enum StdVerifSenderError { + PoisonError, + StoreError(StoreError), + RxDisconnected(StoreAddr), + } + + trait SendBackend: Send { + fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr>; + } + + struct StdSenderBase { + pub ignore_poison_error: bool, + tm_store: Arc>, + tx: S, + } + + impl StdSenderBase { + pub fn new(tm_store: Arc>, tx: S) -> Self { + Self { + ignore_poison_error: false, + tm_store, + tx, + } } } -} -#[cfg(feature = "std")] -unsafe impl Sync for StdVerifSender {} -#[cfg(feature = "std")] -unsafe impl Send for StdVerifSender {} + impl SendBackend for mpsc::Sender { + fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> { + self.send(addr).map_err(|_| addr) + } + } -#[cfg(feature = "std")] -#[derive(Debug, Eq, PartialEq)] -pub enum StdVerifSenderError { - PoisonError, - StoreError(StoreError), - RxDisconnected(StoreAddr), -} + pub struct StdVerifSender { + base: StdSenderBase>, + } -#[cfg(feature = "std")] -impl VerificationSender for StdVerifSender { - fn send_verification_tm( - &mut self, - tm: PusTm, - ) -> Result<(), VerificationError> { - let operation = |mut mg: RwLockWriteGuard| { - let (addr, buf) = mg - .free_element(tm.len_packed()) - .map_err(|e| VerificationError::SendError(StdVerifSenderError::StoreError(e)))?; - tm.write_to(buf).map_err(VerificationError::PusError)?; - self.tx.send(addr).map_err(|_| { - VerificationError::SendError(StdVerifSenderError::RxDisconnected(addr)) - })?; - Ok(()) - }; - match self.tm_store.write() { - Ok(lock) => operation(lock), - Err(poison_error) => { - if self.ignore_poison_error { - operation(poison_error.into_inner()) - } else { - Err(VerificationError::SendError( - StdVerifSenderError::PoisonError, - )) + impl StdVerifSender { + pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { + Self { + base: StdSenderBase::new(tm_store, tx), + } + } + } + + //noinspection RsTraitImplementation + impl VerificationSender for StdVerifSender { + delegate!( + to self.base { + fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError>; + } + ); + } + unsafe impl Sync for StdVerifSender {} + unsafe impl Send for StdVerifSender {} + + impl SendBackend for crossbeam_channel::Sender { + fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> { + self.send(addr).map_err(|_| addr) + } + } + + pub struct CrossbeamVerifSender { + base: StdSenderBase>, + } + + impl CrossbeamVerifSender { + pub fn new( + tm_store: Arc>, + tx: crossbeam_channel::Sender, + ) -> Self { + Self { + base: StdSenderBase::new(tm_store, tx), + } + } + } + + //noinspection RsTraitImplementation + impl VerificationSender for CrossbeamVerifSender { + delegate!( + to self.base { + fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError>; + } + ); + } + + unsafe impl Sync for CrossbeamVerifSender {} + unsafe impl Send for CrossbeamVerifSender {} + + impl VerificationSender for StdSenderBase { + fn send_verification_tm( + &mut self, + tm: PusTm, + ) -> Result<(), VerificationError> { + let operation = |mut mg: RwLockWriteGuard| { + let (addr, buf) = mg.free_element(tm.len_packed()).map_err(|e| { + VerificationError::SendError(StdVerifSenderError::StoreError(e)) + })?; + tm.write_to(buf).map_err(VerificationError::PusError)?; + self.tx.send(addr).map_err(|_| { + VerificationError::SendError(StdVerifSenderError::RxDisconnected(addr)) + })?; + Ok(()) + }; + match self.tm_store.write() { + Ok(lock) => operation(lock), + Err(poison_error) => { + if self.ignore_poison_error { + operation(poison_error.into_inner()) + } else { + Err(VerificationError::SendError( + StdVerifSenderError::PoisonError, + )) + } } } } diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index f80efb8..e1431a4 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -1,13 +1,13 @@ use fsrc_core::pool::{LocalPool, PoolCfg}; use fsrc_core::pus::verification::{ - FailParams, RequestId, StdVerifSender, VerificationReporter, VerificationReporterCfg, + CrossbeamVerifSender, FailParams, RequestId, VerificationReporter, VerificationReporterCfg, }; +use hashbrown::HashMap; use spacepackets::ecss::{EcssEnumU16, EcssEnumU8, PusPacket}; use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; use spacepackets::tm::PusTm; use spacepackets::SpHeader; -use std::sync::atomic::{AtomicU16, Ordering}; -use std::sync::{mpsc, Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex, RwLock}; use std::thread; const TEST_APID: u16 = 0x03; @@ -16,28 +16,55 @@ const PACKETS_SENT: u8 = 8; #[test] fn test_shared_reporter() { - let seq_counter_0 = Arc::new(AtomicU16::new(0)); - let seq_counter_1 = seq_counter_0.clone(); let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8); // Shared pool object to store the verification PUS telemetry let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]); - let shared_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); - let (tx, rx) = mpsc::channel(); - let mut sender_0 = StdVerifSender::new(shared_pool.clone(), tx.clone()); - let mut sender_1 = StdVerifSender::new(shared_pool.clone(), tx); + let shared_tm_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg.clone()))); + let shared_tc_pool_0 = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); + let shared_tc_pool_1 = shared_tc_pool_0.clone(); + let (tx, rx) = crossbeam_channel::bounded(5); + let mut sender_0 = CrossbeamVerifSender::new(shared_tm_pool.clone(), tx.clone()); + let mut sender_1 = CrossbeamVerifSender::new(shared_tm_pool.clone(), tx); let reporter_0 = Arc::new(Mutex::new(VerificationReporter::new(cfg))); let reporter_1 = reporter_0.clone(); - let verif_sender_0 = thread::spawn(move || { - let mut sph = - SpHeader::tc(TEST_APID, seq_counter_0.fetch_add(1, Ordering::SeqCst), 0).unwrap(); + let (tx_tc_0, rx_tc_0) = crossbeam_channel::bounded(3); + let (tx_tc_1, rx_tc_1) = crossbeam_channel::bounded(3); + { + let mut tc_guard = shared_tc_pool_0.write().unwrap(); + let mut sph = SpHeader::tc(TEST_APID, 0, 0).unwrap(); let tc_header = PusTcSecondaryHeader::new_simple(17, 1); - let pus_tc = PusTc::new(&mut sph, tc_header, None, true); + let pus_tc_0 = PusTc::new(&mut sph, tc_header, None, true); + let (addr, mut buf) = tc_guard.free_element(pus_tc_0.len_packed()).unwrap(); + pus_tc_0.write_to(&mut buf).unwrap(); + tx_tc_0.send(addr).unwrap(); + let mut sph = SpHeader::tc(TEST_APID, 1, 0).unwrap(); + let tc_header = PusTcSecondaryHeader::new_simple(5, 1); + let pus_tc_1 = PusTc::new(&mut sph, tc_header, None, true); + let (addr, mut buf) = tc_guard.free_element(pus_tc_0.len_packed()).unwrap(); + pus_tc_1.write_to(&mut buf).unwrap(); + tx_tc_1.send(addr).unwrap(); + } + + let verif_sender_0 = thread::spawn(move || { + let mut tc_buf: [u8; 1024] = [0; 1024]; + let tc_addr = rx_tc_1.recv().unwrap(); + let tc_len; + { + let mut tc_guard = shared_tc_pool_0.write().unwrap(); + let pg = tc_guard.read_with_guard(tc_addr); + let buf = pg.read().unwrap(); + tc_len = buf.len(); + tc_buf[0..tc_len].copy_from_slice(buf); + } + let (tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); + let req_id = RequestId::new(&tc); let mut mg = reporter_0.lock().unwrap(); - let token = mg.add_tc(&pus_tc); + let token = mg.add_tc_with_req_id(req_id); let accepted_token = mg .acceptance_success(token, &mut sender_0, &FIXED_STAMP) .expect("Acceptance success failed"); + // Do some start handling here let started_token = mg .start_success(accepted_token, &mut sender_0, &FIXED_STAMP) .expect("Start success failed"); @@ -48,6 +75,7 @@ fn test_shared_reporter() { EcssEnumU8::new(0), ) .expect("Start success failed"); + // Do some step handling here mg.step_success( &started_token, &mut sender_0, @@ -60,12 +88,19 @@ fn test_shared_reporter() { }); let verif_sender_1 = thread::spawn(move || { - let mut sph = - SpHeader::tc(TEST_APID, seq_counter_1.fetch_add(1, Ordering::SeqCst), 0).unwrap(); - let tc_header = PusTcSecondaryHeader::new_simple(5, 1); - let pus_tc = PusTc::new(&mut sph, tc_header, None, true); + let mut tc_buf: [u8; 1024] = [0; 1024]; + let tc_addr = rx_tc_0.recv().unwrap(); + let tc_len; + { + let mut tc_guard = shared_tc_pool_1.write().unwrap(); + let pg = tc_guard.read_with_guard(tc_addr); + let buf = pg.read().unwrap(); + tc_len = buf.len(); + tc_buf[0..tc_len].copy_from_slice(buf); + } + let (tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); let mut mg = reporter_1.lock().unwrap(); - let token = mg.add_tc(&pus_tc); + let token = mg.add_tc(&tc); let accepted_token = mg .acceptance_success(token, &mut sender_1, &FIXED_STAMP) .expect("Acceptance success failed"); @@ -81,9 +116,10 @@ fn test_shared_reporter() { let verif_receiver = thread::spawn(move || { let mut packet_counter = 0; let mut tm_buf: [u8; 1024] = [0; 1024]; + let mut verif_map = HashMap::new(); while packet_counter < PACKETS_SENT { let verif_addr = rx.recv().expect("Error receiving verification packet"); - let mut rg = shared_pool.write().expect("Error locking shared pool"); + let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); let store_guard = rg.read_with_guard(verif_addr); let slice = store_guard.read().expect("Error reading TM slice"); let tm_len = slice.len(); @@ -96,14 +132,29 @@ fn test_shared_reporter() { &pus_tm.source_data().expect("Invalid TM source data")[0..RequestId::SIZE_AS_BYTES], ) .unwrap(); - println!( - "Received PUS Verification TM[{},{}] for request ID {:#08x}", - pus_tm.service(), - pus_tm.subservice(), - req_id.raw() - ); + if !verif_map.contains_key(&req_id) { + let mut content = Vec::new(); + content.push(pus_tm.subservice()); + verif_map.insert(req_id, content); + } else { + let content = verif_map.get_mut(&req_id).unwrap(); + content.push(pus_tm.subservice()) + } packet_counter += 1; } + for (_req_id, content) in verif_map { + if content.len() == 3 { + assert_eq!(content[0], 1); + assert_eq!(content[1], 3); + assert_eq!(content[2], 8); + } else { + assert_eq!(content[0], 1); + assert_eq!(content[1], 3); + assert_eq!(content[2], 5); + assert_eq!(content[3], 5); + assert_eq!(content[4], 7); + } + } }); verif_sender_0.join().expect("Joining thread 0 failed"); verif_sender_1.join().expect("Joining thread 1 failed"); From d0824f1d6873443576c5bb8297d627d978cc3e39 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 11:08:11 +0200 Subject: [PATCH 21/43] examplke stuck somewhere --- fsrc-core/tests/verification_test.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index e1431a4..329f3f5 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -1,3 +1,5 @@ +extern crate core; + use fsrc_core::pool::{LocalPool, PoolCfg}; use fsrc_core::pus::verification::{ CrossbeamVerifSender, FailParams, RequestId, VerificationReporter, VerificationReporterCfg, @@ -27,6 +29,8 @@ fn test_shared_reporter() { let mut sender_1 = CrossbeamVerifSender::new(shared_tm_pool.clone(), tx); let reporter_0 = Arc::new(Mutex::new(VerificationReporter::new(cfg))); let reporter_1 = reporter_0.clone(); + let req_id_0; + let req_id_1; let (tx_tc_0, rx_tc_0) = crossbeam_channel::bounded(3); let (tx_tc_1, rx_tc_1) = crossbeam_channel::bounded(3); @@ -35,12 +39,14 @@ fn test_shared_reporter() { let mut sph = SpHeader::tc(TEST_APID, 0, 0).unwrap(); let tc_header = PusTcSecondaryHeader::new_simple(17, 1); let pus_tc_0 = PusTc::new(&mut sph, tc_header, None, true); + req_id_0 = RequestId::new(&pus_tc_0); let (addr, mut buf) = tc_guard.free_element(pus_tc_0.len_packed()).unwrap(); pus_tc_0.write_to(&mut buf).unwrap(); tx_tc_0.send(addr).unwrap(); let mut sph = SpHeader::tc(TEST_APID, 1, 0).unwrap(); let tc_header = PusTcSecondaryHeader::new_simple(5, 1); let pus_tc_1 = PusTc::new(&mut sph, tc_header, None, true); + req_id_1 = RequestId::new(&pus_tc_1); let (addr, mut buf) = tc_guard.free_element(pus_tc_0.len_packed()).unwrap(); pus_tc_1.write_to(&mut buf).unwrap(); tx_tc_1.send(addr).unwrap(); @@ -48,7 +54,7 @@ fn test_shared_reporter() { let verif_sender_0 = thread::spawn(move || { let mut tc_buf: [u8; 1024] = [0; 1024]; - let tc_addr = rx_tc_1.recv().unwrap(); + let tc_addr = rx_tc_0.recv().unwrap(); let tc_len; { let mut tc_guard = shared_tc_pool_0.write().unwrap(); @@ -57,10 +63,9 @@ fn test_shared_reporter() { tc_len = buf.len(); tc_buf[0..tc_len].copy_from_slice(buf); } - let (tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); - let req_id = RequestId::new(&tc); + let (_tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); let mut mg = reporter_0.lock().unwrap(); - let token = mg.add_tc_with_req_id(req_id); + let token = mg.add_tc_with_req_id(req_id_0); let accepted_token = mg .acceptance_success(token, &mut sender_0, &FIXED_STAMP) .expect("Acceptance success failed"); @@ -89,7 +94,7 @@ fn test_shared_reporter() { let verif_sender_1 = thread::spawn(move || { let mut tc_buf: [u8; 1024] = [0; 1024]; - let tc_addr = rx_tc_0.recv().unwrap(); + let tc_addr = rx_tc_1.recv().unwrap(); let tc_len; { let mut tc_guard = shared_tc_pool_1.write().unwrap(); @@ -142,17 +147,19 @@ fn test_shared_reporter() { } packet_counter += 1; } - for (_req_id, content) in verif_map { - if content.len() == 3 { + for (req_id, content) in verif_map { + if req_id == req_id_0 { assert_eq!(content[0], 1); assert_eq!(content[1], 3); assert_eq!(content[2], 8); - } else { + } else if req_id == req_id_1 { assert_eq!(content[0], 1); assert_eq!(content[1], 3); assert_eq!(content[2], 5); assert_eq!(content[3], 5); assert_eq!(content[4], 7); + } else { + panic!("Unexpected request ID {:?}", req_id); } } }); From d679f1525c98e63dbed83cc8f01a8d3250a16807 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 13:37:09 +0200 Subject: [PATCH 22/43] use recv with timeout --- fsrc-core/tests/verification_test.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index 329f3f5..5fa78e0 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -11,6 +11,7 @@ use spacepackets::tm::PusTm; use spacepackets::SpHeader; use std::sync::{Arc, Mutex, RwLock}; use std::thread; +use std::time::Duration; const TEST_APID: u16 = 0x03; const FIXED_STAMP: [u8; 7] = [0; 7]; @@ -54,7 +55,9 @@ fn test_shared_reporter() { let verif_sender_0 = thread::spawn(move || { let mut tc_buf: [u8; 1024] = [0; 1024]; - let tc_addr = rx_tc_0.recv().unwrap(); + let tc_addr = rx_tc_0 + .recv_timeout(Duration::from_millis(20)) + .expect("Receive timeout"); let tc_len; { let mut tc_guard = shared_tc_pool_0.write().unwrap(); @@ -94,7 +97,9 @@ fn test_shared_reporter() { let verif_sender_1 = thread::spawn(move || { let mut tc_buf: [u8; 1024] = [0; 1024]; - let tc_addr = rx_tc_1.recv().unwrap(); + let tc_addr = rx_tc_1 + .recv_timeout(Duration::from_millis(20)) + .expect("Receive timeout"); let tc_len; { let mut tc_guard = shared_tc_pool_1.write().unwrap(); @@ -123,7 +128,9 @@ fn test_shared_reporter() { let mut tm_buf: [u8; 1024] = [0; 1024]; let mut verif_map = HashMap::new(); while packet_counter < PACKETS_SENT { - let verif_addr = rx.recv().expect("Error receiving verification packet"); + let verif_addr = rx + .recv_timeout(Duration::from_millis(30)) + .expect("Packet reception timeout"); let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); let store_guard = rg.read_with_guard(verif_addr); let slice = store_guard.read().expect("Error reading TM slice"); From 2bb85826b72897a3dff1130772457864473bc963 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 14:28:55 +0200 Subject: [PATCH 23/43] something is still problematic --- fsrc-core/tests/verification_test.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index 5fa78e0..1bc85e9 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -52,7 +52,6 @@ fn test_shared_reporter() { pus_tc_1.write_to(&mut buf).unwrap(); tx_tc_1.send(addr).unwrap(); } - let verif_sender_0 = thread::spawn(move || { let mut tc_buf: [u8; 1024] = [0; 1024]; let tc_addr = rx_tc_0 @@ -67,7 +66,7 @@ fn test_shared_reporter() { tc_buf[0..tc_len].copy_from_slice(buf); } let (_tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); - let mut mg = reporter_0.lock().unwrap(); + let mut mg = reporter_0.lock().expect("Locking mutex failed"); let token = mg.add_tc_with_req_id(req_id_0); let accepted_token = mg .acceptance_success(token, &mut sender_0, &FIXED_STAMP) @@ -109,7 +108,7 @@ fn test_shared_reporter() { tc_buf[0..tc_len].copy_from_slice(buf); } let (tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); - let mut mg = reporter_1.lock().unwrap(); + let mut mg = reporter_1.lock().expect("Locking reporter failed"); let token = mg.add_tc(&tc); let accepted_token = mg .acceptance_success(token, &mut sender_1, &FIXED_STAMP) @@ -129,7 +128,7 @@ fn test_shared_reporter() { let mut verif_map = HashMap::new(); while packet_counter < PACKETS_SENT { let verif_addr = rx - .recv_timeout(Duration::from_millis(30)) + .recv_timeout(Duration::from_millis(50)) .expect("Packet reception timeout"); let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); let store_guard = rg.read_with_guard(verif_addr); @@ -155,11 +154,11 @@ fn test_shared_reporter() { packet_counter += 1; } for (req_id, content) in verif_map { - if req_id == req_id_0 { + if req_id == req_id_1 { assert_eq!(content[0], 1); assert_eq!(content[1], 3); assert_eq!(content[2], 8); - } else if req_id == req_id_1 { + } else if req_id == req_id_0 { assert_eq!(content[0], 1); assert_eq!(content[1], 3); assert_eq!(content[2], 5); @@ -172,5 +171,5 @@ fn test_shared_reporter() { }); verif_sender_0.join().expect("Joining thread 0 failed"); verif_sender_1.join().expect("Joining thread 1 failed"); - verif_receiver.join().expect("Joining thread 2 failed") + verif_receiver.join().expect("Joining thread 2 failed"); } From 007fc3bd2881e083ec30f1e7d992060e6eead007 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 14:35:33 +0200 Subject: [PATCH 24/43] trying some more things --- fsrc-core/tests/verification_test.rs | 54 ++++++++++++++++------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index 1bc85e9..96bfaf4 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -66,23 +66,32 @@ fn test_shared_reporter() { tc_buf[0..tc_len].copy_from_slice(buf); } let (_tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); - let mut mg = reporter_0.lock().expect("Locking mutex failed"); - let token = mg.add_tc_with_req_id(req_id_0); - let accepted_token = mg - .acceptance_success(token, &mut sender_0, &FIXED_STAMP) - .expect("Acceptance success failed"); + let accepted_token; + { + let mut mg = reporter_0.lock().expect("Locking mutex failed"); + let token = mg.add_tc_with_req_id(req_id_0); + accepted_token = mg + .acceptance_success(token, &mut sender_0, &FIXED_STAMP) + .expect("Acceptance success failed"); + } // Do some start handling here - let started_token = mg - .start_success(accepted_token, &mut sender_0, &FIXED_STAMP) + let started_token; + { + let mut mg = reporter_0.lock().expect("Locking mutex failed"); + started_token = mg + .start_success(accepted_token, &mut sender_0, &FIXED_STAMP) + .expect("Start success failed"); + // Do some step handling here + mg.step_success( + &started_token, + &mut sender_0, + &FIXED_STAMP, + EcssEnumU8::new(0), + ) .expect("Start success failed"); - mg.step_success( - &started_token, - &mut sender_0, - &FIXED_STAMP, - EcssEnumU8::new(0), - ) - .expect("Start success failed"); - // Do some step handling here + } + // Finish up + let mut mg = reporter_0.lock().expect("Locking mutex failed"); mg.step_success( &started_token, &mut sender_0, @@ -130,13 +139,14 @@ fn test_shared_reporter() { let verif_addr = rx .recv_timeout(Duration::from_millis(50)) .expect("Packet reception timeout"); - let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); - let store_guard = rg.read_with_guard(verif_addr); - let slice = store_guard.read().expect("Error reading TM slice"); - let tm_len = slice.len(); - tm_buf[0..tm_len].copy_from_slice(slice); - drop(store_guard); - drop(rg); + let tm_len; + { + let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); + let store_guard = rg.read_with_guard(verif_addr); + let slice = store_guard.read().expect("Error reading TM slice"); + tm_len = slice.len(); + tm_buf[0..tm_len].copy_from_slice(slice); + } let (pus_tm, _) = PusTm::new_from_raw_slice(&tm_buf[0..tm_len], 7) .expect("Error reading verification TM"); let req_id = RequestId::from_bytes( From 8069f18b730bbc9d2e7abd41c510d9e18e48c9a9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 14:48:43 +0200 Subject: [PATCH 25/43] this drop is really important --- fsrc-core/src/pus/verification.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index b81a47d..0ae81c0 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -754,6 +754,7 @@ mod stdmod { VerificationError::SendError(StdVerifSenderError::StoreError(e)) })?; tm.write_to(buf).map_err(VerificationError::PusError)?; + drop(mg); self.tx.send(addr).map_err(|_| { VerificationError::SendError(StdVerifSenderError::RxDisconnected(addr)) })?; From f0a9f8b5912baffb910e4911d06405e71efc0ca6 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 14:57:44 +0200 Subject: [PATCH 26/43] use helper struct --- fsrc-core/src/pus/verification.rs | 4 ++ fsrc-core/tests/verification_test.rs | 62 +++++++++++++++------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 0ae81c0..34e1f1b 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -692,6 +692,8 @@ mod stdmod { base: StdSenderBase>, } + /// Verification sender with a [mpsc::Sender] backend. + /// It implements the [VerificationSender] trait to be used as PUS Verification TM sender. impl StdVerifSender { pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { Self { @@ -717,6 +719,8 @@ mod stdmod { } } + /// Verification sender with a [crossbeam_channel::Sender] backend. + /// It implements the [VerificationSender] trait to be used as PUS Verification TM sender pub struct CrossbeamVerifSender { base: StdSenderBase>, } diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index 96bfaf4..df9378d 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -2,7 +2,8 @@ extern crate core; use fsrc_core::pool::{LocalPool, PoolCfg}; use fsrc_core::pus::verification::{ - CrossbeamVerifSender, FailParams, RequestId, VerificationReporter, VerificationReporterCfg, + CrossbeamVerifSender, FailParams, RequestId, VerificationReporterCfg, + VerificationReporterWithSender, }; use hashbrown::HashMap; use spacepackets::ecss::{EcssEnumU16, EcssEnumU8, PusPacket}; @@ -17,6 +18,13 @@ const TEST_APID: u16 = 0x03; const FIXED_STAMP: [u8; 7] = [0; 7]; const PACKETS_SENT: u8 = 8; +/// This test also shows how the verification report could be used in a multi-threaded context, +/// wrapping it into an [Arc] and [Mutex] and then passing it to two threads. +/// +/// - The first thread generates a acceptance, a start, two steps and one completion report +/// - The second generates an acceptance and start success report and a completion failure +/// - The third thread is the verification receiver. In the test case, it verifies the other two +/// threads have sent the correct expected verification reports #[test] fn test_shared_reporter() { let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8); @@ -26,10 +34,14 @@ fn test_shared_reporter() { let shared_tc_pool_0 = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); let shared_tc_pool_1 = shared_tc_pool_0.clone(); let (tx, rx) = crossbeam_channel::bounded(5); - let mut sender_0 = CrossbeamVerifSender::new(shared_tm_pool.clone(), tx.clone()); - let mut sender_1 = CrossbeamVerifSender::new(shared_tm_pool.clone(), tx); - let reporter_0 = Arc::new(Mutex::new(VerificationReporter::new(cfg))); - let reporter_1 = reporter_0.clone(); + let sender = CrossbeamVerifSender::new(shared_tm_pool.clone(), tx.clone()); + let reporter_with_sender_0 = Arc::new(Mutex::new(VerificationReporterWithSender::new( + cfg, + Box::new(sender), + ))); + let reporter_with_sender_1 = reporter_with_sender_0.clone(); + // For test purposes, we retrieve the request ID from the TCs and pass them to the receiver + // tread. let req_id_0; let req_id_1; @@ -68,38 +80,28 @@ fn test_shared_reporter() { let (_tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); let accepted_token; { - let mut mg = reporter_0.lock().expect("Locking mutex failed"); + let mut mg = reporter_with_sender_0.lock().expect("Locking mutex failed"); let token = mg.add_tc_with_req_id(req_id_0); accepted_token = mg - .acceptance_success(token, &mut sender_0, &FIXED_STAMP) + .acceptance_success(token, &FIXED_STAMP) .expect("Acceptance success failed"); } // Do some start handling here let started_token; { - let mut mg = reporter_0.lock().expect("Locking mutex failed"); + let mut mg = reporter_with_sender_0.lock().expect("Locking mutex failed"); started_token = mg - .start_success(accepted_token, &mut sender_0, &FIXED_STAMP) + .start_success(accepted_token, &FIXED_STAMP) .expect("Start success failed"); // Do some step handling here - mg.step_success( - &started_token, - &mut sender_0, - &FIXED_STAMP, - EcssEnumU8::new(0), - ) - .expect("Start success failed"); + mg.step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(0)) + .expect("Start success failed"); } // Finish up - let mut mg = reporter_0.lock().expect("Locking mutex failed"); - mg.step_success( - &started_token, - &mut sender_0, - &FIXED_STAMP, - EcssEnumU8::new(1), - ) - .expect("Start success failed"); - mg.completion_success(started_token, &mut sender_0, &FIXED_STAMP) + let mut mg = reporter_with_sender_0.lock().expect("Locking mutex failed"); + mg.step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(1)) + .expect("Start success failed"); + mg.completion_success(started_token, &FIXED_STAMP) .expect("Completion success failed"); }); @@ -117,17 +119,19 @@ fn test_shared_reporter() { tc_buf[0..tc_len].copy_from_slice(buf); } let (tc, _) = PusTc::new_from_raw_slice(&tc_buf[0..tc_len]).unwrap(); - let mut mg = reporter_1.lock().expect("Locking reporter failed"); + let mut mg = reporter_with_sender_1 + .lock() + .expect("Locking reporter failed"); let token = mg.add_tc(&tc); let accepted_token = mg - .acceptance_success(token, &mut sender_1, &FIXED_STAMP) + .acceptance_success(token, &FIXED_STAMP) .expect("Acceptance success failed"); let started_token = mg - .start_success(accepted_token, &mut sender_1, &FIXED_STAMP) + .start_success(accepted_token, &FIXED_STAMP) .expect("Start success failed"); let fail_code = EcssEnumU16::new(2); let params = FailParams::new(&FIXED_STAMP, &fail_code, None); - mg.completion_failure(started_token, &mut sender_1, params) + mg.completion_failure(started_token, params) .expect("Completion success failed"); }); From c46ef46e7de22782a9b68e98e80fc36f97e0102f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 15:19:16 +0200 Subject: [PATCH 27/43] added basic doctest --- fsrc-core/src/pus/verification.rs | 63 ++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 34e1f1b..0eb29a6 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -7,8 +7,67 @@ //! an API which uses type-state programming to avoid calling the verification steps in //! an invalid order. //! -//! # Example -//! TODO: Cross Ref integration test which will be provided +//! # Examples +//! +//! ``` +//! use std::sync::{Arc, RwLock}; +//! use std::time::Duration; +//! use fsrc_core::pool::{LocalPool, PoolCfg}; +//! use fsrc_core::pus::verification::{CrossbeamVerifSender, VerificationReporterCfg, VerificationReporterWithSender}; +//! use spacepackets::ecss::PusPacket; +//! use spacepackets::SpHeader; +//! use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; +//! use spacepackets::tm::PusTm; +//! +//! const EMPTY_STAMP: [u8; 7] = [0; 7]; +//! const TEST_APID: u16 = 0x02; +//! +//! let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]); +//! let shared_tm_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg.clone()))); +//! let (verif_tx, verif_rx) = crossbeam_channel::bounded(10); +//! let sender = CrossbeamVerifSender::new(shared_tm_pool.clone(), verif_tx); +//! let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8); +//! let mut reporter = VerificationReporterWithSender::new(cfg , Box::new(sender)); +//! +//! let mut sph = SpHeader::tc(TEST_APID, 0, 0).unwrap(); +//! let tc_header = PusTcSecondaryHeader::new_simple(17, 1); +//! let pus_tc_0 = PusTc::new(&mut sph, tc_header, None, true); +//! let init_token = reporter.add_tc(&pus_tc_0); +//! +//! // Complete success sequence for a telecommand +//! let accepted_token = reporter.acceptance_success(init_token, &EMPTY_STAMP).unwrap(); +//! let started_token = reporter.start_success(accepted_token, &EMPTY_STAMP).unwrap(); +//! reporter.completion_success(started_token, &EMPTY_STAMP).unwrap(); +//! +//! // Verify it arrives correctly on receiver end +//! let mut tm_buf: [u8; 1024] = [0; 1024]; +//! let mut packet_idx = 0; +//! while packet_idx < 3 { +//! let addr = verif_rx.recv_timeout(Duration::from_millis(10)).unwrap(); +//! let tm_len; +//! { +//! let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); +//! let store_guard = rg.read_with_guard(addr); +//! let slice = store_guard.read().expect("Error reading TM slice"); +//! tm_len = slice.len(); +//! tm_buf[0..tm_len].copy_from_slice(slice); +//! } +//! let (pus_tm, _) = PusTm::new_from_raw_slice(&tm_buf[0..tm_len], 7) +//! .expect("Error reading verification TM"); +//! if packet_idx == 0 { +//! assert_eq!(pus_tm.subservice(), 1); +//! } else if packet_idx == 1 { +//! assert_eq!(pus_tm.subservice(), 3); +//! } else if packet_idx == 2 { +//! assert_eq!(pus_tm.subservice(), 7); +//! } +//! packet_idx += 1; +//! } +//! ``` +//! +//! The [integration test](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/obsw-client-example/fsrc-core/tests/verification_test.rs) +//! for the verification module contains examples how this module could be used in a more complex +//! context involving multiple threads use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; From 8a688294cf714a4704ede181504702c3a978a7a5 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 15:21:44 +0200 Subject: [PATCH 28/43] now only an example using a central tm creator is required --- fsrc-core/src/pus/verification.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 0eb29a6..e3646bb 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -9,6 +9,9 @@ //! //! # Examples //! +//! Basic single-threaded example where a full success sequence for a given ping telecommand is +//! executed. Note that the verification port could also be done in a separate thread. +//! //! ``` //! use std::sync::{Arc, RwLock}; //! use std::time::Duration; From 904eb607b1ae835b7c90b3264fc0582ce75310f3 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 7 Sep 2022 15:36:56 +0200 Subject: [PATCH 29/43] remove unneeded extern decl --- fsrc-core/tests/verification_test.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index df9378d..4576471 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -1,5 +1,3 @@ -extern crate core; - use fsrc_core::pool::{LocalPool, PoolCfg}; use fsrc_core::pus::verification::{ CrossbeamVerifSender, FailParams, RequestId, VerificationReporterCfg, From a2c1a38b48914d594ff8ac715927617e6e9249ba Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 8 Sep 2022 00:30:01 +0200 Subject: [PATCH 30/43] add basic test bin --- Cargo.lock | 1 + fsrc-example/Cargo.toml | 3 +++ fsrc-example/src/bin/test.rs | 44 ++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 fsrc-example/src/bin/test.rs diff --git a/Cargo.lock b/Cargo.lock index 1d8bc60..a09bb11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,6 +240,7 @@ dependencies = [ name = "fsrc-example" version = "0.1.0" dependencies = [ + "crossbeam-channel", "fsrc-core", "spacepackets", ] diff --git a/fsrc-example/Cargo.toml b/fsrc-example/Cargo.toml index 8f29e3b..ede00be 100644 --- a/fsrc-example/Cargo.toml +++ b/fsrc-example/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" authors = ["Robin Mueller "] +[dependencies] +crossbeam-channel = "0.5" + [dependencies.spacepackets] path = "../spacepackets" diff --git a/fsrc-example/src/bin/test.rs b/fsrc-example/src/bin/test.rs new file mode 100644 index 0000000..026ccfc --- /dev/null +++ b/fsrc-example/src/bin/test.rs @@ -0,0 +1,44 @@ +use crossbeam_channel::{bounded, Receiver, Sender}; +use std::thread; + +trait FieldDataProvider: Send { + fn get_data(&self) -> &[u8]; +} + +struct FixedFieldDataWrapper { + data: [u8; 8], +} + +impl FixedFieldDataWrapper { + pub fn from_two_u32(p0: u32, p1: u32) -> Self { + let mut data = [0; 8]; + data[0..4].copy_from_slice(p0.to_be_bytes().as_slice()); + data[4..8].copy_from_slice(p1.to_be_bytes().as_slice()); + Self { data } + } +} + +impl FieldDataProvider for FixedFieldDataWrapper { + fn get_data(&self) -> &[u8] { + self.data.as_slice() + } +} + +type FieldDataTraitObj = Box; + +fn main() { + let (s0, r0): ( + Sender, + Receiver, + ) = bounded(5); + let data_wrapper = FixedFieldDataWrapper::from_two_u32(2, 3); + s0.send(Box::new(data_wrapper)).unwrap(); + let jh0 = thread::spawn(move || { + let data = r0.recv().unwrap(); + let raw = data.get_data(); + println!("Received data {:?}", raw); + }); + let jh1 = thread::spawn(|| {}); + jh0.join().unwrap(); + jh1.join().unwrap(); +} From f5a45c008e62059e43bc7051f93be6f672bb91a7 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 10 Sep 2022 13:34:04 +0200 Subject: [PATCH 31/43] this is complicated --- Cargo.lock | 16 +- fsrc-core/Cargo.toml | 2 +- fsrc-core/src/pool.rs | 215 +++++++++++++++------------ fsrc-core/src/pus/verification.rs | 46 +++--- fsrc-core/tests/pool_test.rs | 2 +- fsrc-core/tests/verification_test.rs | 5 +- fsrc-example/Cargo.toml | 1 + fsrc-example/src/bin/obsw/main.rs | 33 +++- fsrc-example/src/bin/obsw/pus.rs | 22 +-- fsrc-example/src/bin/obsw/tmtc.rs | 31 ++-- fsrc-example/src/bin/test.rs | 5 +- 11 files changed, 227 insertions(+), 151 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a09bb11..8d3a336 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,6 +202,17 @@ dependencies = [ "syn", ] +[[package]] +name = "delegate" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "082a24a9967533dc5d743c602157637116fc1b52806d694a5a45e6f32567fcdd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "downcast-rs" version = "1.2.0" @@ -224,7 +235,7 @@ version = "0.1.0" dependencies = [ "bus", "crossbeam-channel", - "delegate", + "delegate 0.8.0", "downcast-rs", "hashbrown", "num-traits", @@ -241,6 +252,7 @@ name = "fsrc-example" version = "0.1.0" dependencies = [ "crossbeam-channel", + "delegate 0.8.0", "fsrc-core", "spacepackets", ] @@ -573,7 +585,7 @@ version = "0.1.0" dependencies = [ "chrono", "crc", - "delegate", + "delegate 0.7.0", "num-traits", "postcard", "serde", diff --git a/fsrc-core/Cargo.toml b/fsrc-core/Cargo.toml index 160c727..482f2ab 100644 --- a/fsrc-core/Cargo.toml +++ b/fsrc-core/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] thiserror = "1.0" -delegate = "0.7.0" +delegate = "0.8.0" hashbrown = "0.12.3" [dependencies.num-traits] diff --git a/fsrc-core/src/pool.rs b/fsrc-core/src/pool.rs index e1a00a7..66b68a5 100644 --- a/fsrc-core/src/pool.rs +++ b/fsrc-core/src/pool.rs @@ -13,7 +13,7 @@ //! # Example //! //! ``` -//! use fsrc_core::pool::{LocalPool, PoolCfg}; +//! use fsrc_core::pool::{LocalPool, PoolCfg, PoolProvider}; //! //! // 4 buckets of 4 bytes, 2 of 8 bytes and 1 of 16 bytes //! let pool_cfg = PoolCfg::new(vec![(4, 4), (2, 8), (1, 16)]); @@ -78,9 +78,18 @@ use alloc::string::String; use alloc::vec; use alloc::vec::Vec; use delegate::delegate; +#[cfg(feature = "std")] +use std::boxed::Box; +#[cfg(feature = "std")] +use std::sync::{Arc, RwLock}; type NumBlocks = u16; +#[cfg(feature = "std")] +pub type ShareablePoolProvider = Box; +#[cfg(feature = "std")] +pub type SharedPool = Arc>; + /// Configuration structure of the [local pool][LocalPool] /// /// # Parameters @@ -151,10 +160,52 @@ pub enum StoreError { InternalError(String), } +pub trait PoolProvider { + /// Add new data to the pool. The provider should attempt to reserve a memory block with the + /// appropriate size and then copy the given data to the block. Yields a [StoreAddr] which can + /// be used to access the data stored in the pool + fn add(&mut self, data: &[u8]) -> Result; + + /// The provider should attempt to reserve a free memory block with the appropriate size and + /// then return a mutable reference to it. Yields a [StoreAddr] which can be used to access + /// the data stored in the pool + fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError>; + + /// Modify data added previously using a given [StoreAddr] by yielding a mutable reference + /// to it + fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError>; + + /// This function behaves like [Self::modify], but consumes the provided address and returns a + /// RAII conformant guard object. + /// + /// Unless the guard [PoolRwGuard::release] method is called, the data for the + /// given address will be deleted automatically when the guard is dropped. + /// This can prevent memory leaks. Users can read (and modify) the data and release the guard + /// if the data in the store is valid for further processing. If the data is faulty, no + /// manual deletion is necessary when returning from a processing function prematurely. + fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard; + + /// Read data by yielding a read-only reference given a [StoreAddr] + fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError>; + + /// This function behaves like [Self::read], but consumes the provided address and returns a + /// RAII conformant guard object. + /// + /// Unless the guard [PoolRwGuard::release] method is called, the data for the + /// given address will be deleted automatically when the guard is dropped. + /// This can prevent memory leaks. Users can read the data and release the guard + /// if the data in the store is valid for further processing. If the data is faulty, no + /// manual deletion is necessary when returning from a processing function prematurely. + fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard; + + /// Delete data inside the pool given a [StoreAddr] + fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError>; + fn has_element_at(&self, addr: &StoreAddr) -> Result; +} + impl LocalPool { const STORE_FREE: PoolSize = PoolSize::MAX; const MAX_SIZE: PoolSize = Self::STORE_FREE - 1; - /// Create a new local pool from the [given configuration][PoolCfg]. This function will sanitize /// the given configuration as well. pub fn new(mut cfg: PoolCfg) -> LocalPool { @@ -175,96 +226,6 @@ impl LocalPool { local_pool } - /// Add new data to the pool. It will attempt to reserve a memory block with the appropriate - /// size and then copy the given data to the block. Yields a [StoreAddr] which can be used - /// to access the data stored in the pool - pub fn add(&mut self, data: &[u8]) -> Result { - let data_len = data.len(); - if data_len > Self::MAX_SIZE { - return Err(StoreError::DataTooLarge(data_len)); - } - let addr = self.reserve(data_len)?; - self.write(&addr, data)?; - Ok(addr) - } - - /// Reserves a free memory block with the appropriate size and returns a mutable reference - /// to it. Yields a [StoreAddr] which can be used to access the data stored in the pool - pub fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError> { - if len > Self::MAX_SIZE { - return Err(StoreError::DataTooLarge(len)); - } - let addr = self.reserve(len)?; - let raw_pos = self.raw_pos(&addr).unwrap(); - let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + len]; - Ok((addr, block)) - } - - /// Modify data added previously using a given [StoreAddr] by yielding a mutable reference - /// to it - pub fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError> { - let curr_size = self.addr_check(addr)?; - let raw_pos = self.raw_pos(addr).unwrap(); - let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..curr_size]; - Ok(block) - } - - /// This function behaves like [Self::modify], but consumes the provided address and returns a - /// RAII conformant guard object. - /// - /// Unless the guard [PoolRwGuard::release] method is called, the data for the - /// given address will be deleted automatically when the guard is dropped. - /// This can prevent memory leaks. Users can read (and modify) the data and release the guard - /// if the data in the store is valid for further processing. If the data is faulty, no - /// manual deletion is necessary when returning from a processing function prematurely. - pub fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard { - PoolRwGuard::new(self, addr) - } - - /// Read data by yielding a read-only reference given a [StoreAddr] - pub fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError> { - let curr_size = self.addr_check(addr)?; - let raw_pos = self.raw_pos(addr).unwrap(); - let block = &self.pool.get(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size]; - Ok(block) - } - - /// This function behaves like [Self::read], but consumes the provided address and returns a - /// RAII conformant guard object. - /// - /// Unless the guard [PoolRwGuard::release] method is called, the data for the - /// given address will be deleted automatically when the guard is dropped. - /// This can prevent memory leaks. Users can read the data and release the guard - /// if the data in the store is valid for further processing. If the data is faulty, no - /// manual deletion is necessary when returning from a processing function prematurely. - pub fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard { - PoolGuard::new(self, addr) - } - - /// Delete data inside the pool given a [StoreAddr] - pub fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError> { - self.addr_check(&addr)?; - let block_size = self.pool_cfg.cfg.get(addr.pool_idx as usize).unwrap().1; - let raw_pos = self.raw_pos(&addr).unwrap(); - let block = - &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + block_size]; - let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap(); - size_list[addr.packet_idx as usize] = Self::STORE_FREE; - block.fill(0); - Ok(()) - } - - pub fn has_element_at(&self, addr: &StoreAddr) -> Result { - self.validate_addr(addr)?; - let pool_idx = addr.pool_idx as usize; - let size_list = self.sizes_lists.get(pool_idx).unwrap(); - let curr_size = size_list[addr.packet_idx as usize]; - if curr_size == Self::STORE_FREE { - return Ok(false); - } - Ok(true) - } - fn addr_check(&self, addr: &StoreAddr) -> Result { self.validate_addr(addr)?; let pool_idx = addr.pool_idx as usize; @@ -355,6 +316,73 @@ impl LocalPool { } } +impl PoolProvider for LocalPool { + fn add(&mut self, data: &[u8]) -> Result { + let data_len = data.len(); + if data_len > Self::MAX_SIZE { + return Err(StoreError::DataTooLarge(data_len)); + } + let addr = self.reserve(data_len)?; + self.write(&addr, data)?; + Ok(addr) + } + + fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError> { + if len > Self::MAX_SIZE { + return Err(StoreError::DataTooLarge(len)); + } + let addr = self.reserve(len)?; + let raw_pos = self.raw_pos(&addr).unwrap(); + let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + len]; + Ok((addr, block)) + } + + fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError> { + let curr_size = self.addr_check(addr)?; + let raw_pos = self.raw_pos(addr).unwrap(); + let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..curr_size]; + Ok(block) + } + + fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard { + PoolRwGuard::new(self, addr) + } + + fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError> { + let curr_size = self.addr_check(addr)?; + let raw_pos = self.raw_pos(addr).unwrap(); + let block = &self.pool.get(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size]; + Ok(block) + } + + fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard { + PoolGuard::new(self, addr) + } + + fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError> { + self.addr_check(&addr)?; + let block_size = self.pool_cfg.cfg.get(addr.pool_idx as usize).unwrap().1; + let raw_pos = self.raw_pos(&addr).unwrap(); + let block = + &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + block_size]; + let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap(); + size_list[addr.packet_idx as usize] = Self::STORE_FREE; + block.fill(0); + Ok(()) + } + + fn has_element_at(&self, addr: &StoreAddr) -> Result { + self.validate_addr(addr)?; + let pool_idx = addr.pool_idx as usize; + let size_list = self.sizes_lists.get(pool_idx).unwrap(); + let curr_size = size_list[addr.packet_idx as usize]; + if curr_size == Self::STORE_FREE { + return Ok(false); + } + Ok(true) + } +} + pub struct PoolGuard<'a> { pool: &'a mut LocalPool, pub addr: StoreAddr, @@ -418,7 +446,8 @@ impl<'a> PoolRwGuard<'a> { #[cfg(test)] mod tests { use crate::pool::{ - LocalPool, PoolCfg, PoolGuard, PoolRwGuard, StoreAddr, StoreError, StoreIdError, + LocalPool, PoolCfg, PoolGuard, PoolProvider, PoolRwGuard, StoreAddr, StoreError, + StoreIdError, }; use std::vec; diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index e3646bb..e53d5c6 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -15,7 +15,7 @@ //! ``` //! use std::sync::{Arc, RwLock}; //! use std::time::Duration; -//! use fsrc_core::pool::{LocalPool, PoolCfg}; +//! use fsrc_core::pool::{LocalPool, PoolCfg, PoolProvider, SharedPool}; //! use fsrc_core::pus::verification::{CrossbeamVerifSender, VerificationReporterCfg, VerificationReporterWithSender}; //! use spacepackets::ecss::PusPacket; //! use spacepackets::SpHeader; @@ -26,7 +26,7 @@ //! const TEST_APID: u16 = 0x02; //! //! let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]); -//! let shared_tm_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg.clone()))); +//! let shared_tm_pool: SharedPool = Arc::new(RwLock::new(Box::new(LocalPool::new(pool_cfg.clone())))); //! let (verif_tx, verif_rx) = crossbeam_channel::bounded(10); //! let sender = CrossbeamVerifSender::new(shared_tm_pool.clone(), verif_tx); //! let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8); @@ -87,7 +87,10 @@ use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; #[cfg(feature = "std")] -pub use stdmod::{CrossbeamVerifSender, StdVerifSender, StdVerifSenderError}; +pub use stdmod::{ + CrossbeamVerifSender, MpscVerifSender, SharedStdVerifReporterWithSender, + StdVerifReporterWithSender, StdVerifSenderError, +}; /// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard /// This field equivalent to the first two bytes of the CCSDS space packet header. @@ -711,11 +714,16 @@ impl VerificationReporterWithSender { #[cfg(feature = "std")] mod stdmod { - use crate::pool::{LocalPool, StoreAddr, StoreError}; - use crate::pus::verification::{VerificationError, VerificationSender}; + use crate::pool::{ShareablePoolProvider, SharedPool, StoreAddr, StoreError}; + use crate::pus::verification::{ + VerificationError, VerificationReporterWithSender, VerificationSender, + }; use delegate::delegate; use spacepackets::tm::PusTm; - use std::sync::{mpsc, Arc, RwLock, RwLockWriteGuard}; + use std::sync::{mpsc, Arc, Mutex, RwLockWriteGuard}; + + pub type StdVerifReporterWithSender = VerificationReporterWithSender; + pub type SharedStdVerifReporterWithSender = Arc>; #[derive(Debug, Eq, PartialEq)] pub enum StdVerifSenderError { @@ -730,12 +738,12 @@ mod stdmod { struct StdSenderBase { pub ignore_poison_error: bool, - tm_store: Arc>, + tm_store: SharedPool, tx: S, } impl StdSenderBase { - pub fn new(tm_store: Arc>, tx: S) -> Self { + pub fn new(tm_store: SharedPool, tx: S) -> Self { Self { ignore_poison_error: false, tm_store, @@ -744,20 +752,23 @@ mod stdmod { } } + unsafe impl Sync for StdSenderBase {} + unsafe impl Send for StdSenderBase {} + impl SendBackend for mpsc::Sender { fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> { self.send(addr).map_err(|_| addr) } } - pub struct StdVerifSender { + pub struct MpscVerifSender { base: StdSenderBase>, } /// Verification sender with a [mpsc::Sender] backend. /// It implements the [VerificationSender] trait to be used as PUS Verification TM sender. - impl StdVerifSender { - pub fn new(tm_store: Arc>, tx: mpsc::Sender) -> Self { + impl MpscVerifSender { + pub fn new(tm_store: SharedPool, tx: mpsc::Sender) -> Self { Self { base: StdSenderBase::new(tm_store, tx), } @@ -765,15 +776,15 @@ mod stdmod { } //noinspection RsTraitImplementation - impl VerificationSender for StdVerifSender { + impl VerificationSender for MpscVerifSender { delegate!( to self.base { fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError>; } ); } - unsafe impl Sync for StdVerifSender {} - unsafe impl Send for StdVerifSender {} + unsafe impl Sync for MpscVerifSender {} + unsafe impl Send for MpscVerifSender {} impl SendBackend for crossbeam_channel::Sender { fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> { @@ -788,10 +799,7 @@ mod stdmod { } impl CrossbeamVerifSender { - pub fn new( - tm_store: Arc>, - tx: crossbeam_channel::Sender, - ) -> Self { + pub fn new(tm_store: SharedPool, tx: crossbeam_channel::Sender) -> Self { Self { base: StdSenderBase::new(tm_store, tx), } @@ -815,7 +823,7 @@ mod stdmod { &mut self, tm: PusTm, ) -> Result<(), VerificationError> { - let operation = |mut mg: RwLockWriteGuard| { + let operation = |mut mg: RwLockWriteGuard| { let (addr, buf) = mg.free_element(tm.len_packed()).map_err(|e| { VerificationError::SendError(StdVerifSenderError::StoreError(e)) })?; diff --git a/fsrc-core/tests/pool_test.rs b/fsrc-core/tests/pool_test.rs index e116130..3187fd9 100644 --- a/fsrc-core/tests/pool_test.rs +++ b/fsrc-core/tests/pool_test.rs @@ -1,4 +1,4 @@ -use fsrc_core::pool::{LocalPool, PoolCfg, PoolGuard, StoreAddr}; +use fsrc_core::pool::{LocalPool, PoolCfg, PoolGuard, PoolProvider, StoreAddr}; use std::ops::DerefMut; use std::sync::mpsc; use std::sync::mpsc::{Receiver, Sender}; diff --git a/fsrc-core/tests/verification_test.rs b/fsrc-core/tests/verification_test.rs index 4576471..22f351e 100644 --- a/fsrc-core/tests/verification_test.rs +++ b/fsrc-core/tests/verification_test.rs @@ -1,4 +1,4 @@ -use fsrc_core::pool::{LocalPool, PoolCfg}; +use fsrc_core::pool::{LocalPool, PoolCfg, PoolProvider, SharedPool}; use fsrc_core::pus::verification::{ CrossbeamVerifSender, FailParams, RequestId, VerificationReporterCfg, VerificationReporterWithSender, @@ -28,7 +28,8 @@ fn test_shared_reporter() { let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8); // Shared pool object to store the verification PUS telemetry let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]); - let shared_tm_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg.clone()))); + let shared_tm_pool: SharedPool = + Arc::new(RwLock::new(Box::new(LocalPool::new(pool_cfg.clone())))); let shared_tc_pool_0 = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); let shared_tc_pool_1 = shared_tc_pool_0.clone(); let (tx, rx) = crossbeam_channel::bounded(5); diff --git a/fsrc-example/Cargo.toml b/fsrc-example/Cargo.toml index ede00be..8bccc84 100644 --- a/fsrc-example/Cargo.toml +++ b/fsrc-example/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Robin Mueller "] [dependencies] crossbeam-channel = "0.5" +delegate = "0.8" [dependencies.spacepackets] path = "../spacepackets" diff --git a/fsrc-example/src/bin/obsw/main.rs b/fsrc-example/src/bin/obsw/main.rs index 728196c..00ad086 100644 --- a/fsrc-example/src/bin/obsw/main.rs +++ b/fsrc-example/src/bin/obsw/main.rs @@ -2,13 +2,16 @@ mod ccsds; mod pus; mod tmtc; -use crate::tmtc::{core_tmtc_task, TmStore}; +use crate::tmtc::{core_tmtc_task, TmStore, PUS_APID}; use fsrc_core::hal::host::udp_server::UdpTcServer; -use fsrc_core::pool::{LocalPool, PoolCfg, StoreAddr}; +use fsrc_core::pool::{LocalPool, PoolCfg, SharedPool, StoreAddr}; +use fsrc_core::pus::verification::{ + MpscVerifSender, VerificationReporterCfg, VerificationReporterWithSender, +}; use fsrc_core::tmtc::CcsdsError; use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT}; use std::net::{IpAddr, SocketAddr}; -use std::sync::{mpsc, Arc, Mutex}; +use std::sync::{mpsc, Arc, Mutex, RwLock}; use std::thread; struct TmFunnel { @@ -19,7 +22,7 @@ struct TmFunnel { struct UdpTmtcServer { udp_tc_server: UdpTcServer>, tm_rx: mpsc::Receiver, - tm_store: Arc>, + tm_store: SharedPool, } unsafe impl Send for UdpTmtcServer {} @@ -28,13 +31,27 @@ fn main() { println!("Running OBSW example"); let pool_cfg = PoolCfg::new(vec![(8, 32), (4, 64), (2, 128)]); let tm_pool = LocalPool::new(pool_cfg); - let tm_store = Arc::new(Mutex::new(TmStore { pool: tm_pool })); + let tm_store: SharedPool = Arc::new(RwLock::new(Box::new(tm_pool))); + let tm_store_helper = TmStore { + pool: tm_store.clone(), + }; let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); - let (tm_creator_tx, tm_funnel_rx) = mpsc::channel(); + let (tm_funnel_tx, tm_funnel_rx) = mpsc::channel(); let (tm_server_tx, tm_server_rx) = mpsc::channel(); - + let sender = MpscVerifSender::new(tm_store.clone(), tm_funnel_tx.clone()); + let verif_cfg = VerificationReporterCfg::new(PUS_APID, 1, 2, 8); + let reporter_with_sender_0 = Arc::new(Mutex::new(VerificationReporterWithSender::new( + verif_cfg, + Box::new(sender), + ))); let jh0 = thread::spawn(move || { - core_tmtc_task(tm_creator_tx.clone(), tm_server_rx, tm_store.clone(), addr); + core_tmtc_task( + tm_funnel_tx.clone(), + tm_server_rx, + tm_store_helper.clone(), + addr, + reporter_with_sender_0, + ); }); let jh1 = thread::spawn(move || { diff --git a/fsrc-example/src/bin/obsw/pus.rs b/fsrc-example/src/bin/obsw/pus.rs index ee7e9cf..eb76d09 100644 --- a/fsrc-example/src/bin/obsw/pus.rs +++ b/fsrc-example/src/bin/obsw/pus.rs @@ -1,23 +1,31 @@ -use crate::TmStore; +use crate::tmtc::TmStore; use fsrc_core::pool::StoreAddr; +use fsrc_core::pus::verification::SharedStdVerifReporterWithSender; use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper; use fsrc_core::tmtc::PusServiceProvider; use spacepackets::tc::{PusTc, PusTcSecondaryHeaderT}; use spacepackets::SpHeader; -use std::sync::{mpsc, Arc, Mutex}; +use std::sync::mpsc; pub struct PusReceiver { pub tm_helper: PusTmWithCdsShortHelper, pub tm_tx: mpsc::Sender, - pub tm_store: Arc>, + pub tm_store: TmStore, + pub verif_reporter: SharedStdVerifReporterWithSender, } impl PusReceiver { - pub fn new(apid: u16, tm_tx: mpsc::Sender, tm_store: Arc>) -> Self { + pub fn new( + apid: u16, + tm_tx: mpsc::Sender, + tm_store: TmStore, + verif_reporter: SharedStdVerifReporterWithSender, + ) -> Self { Self { tm_helper: PusTmWithCdsShortHelper::new(apid), tm_tx, tm_store, + verif_reporter, } } } @@ -44,11 +52,7 @@ impl PusReceiver { println!("Received PUS ping command TC[17,1]"); println!("Sending ping reply PUS TM[17,2]"); let ping_reply = self.tm_helper.create_pus_tm_timestamp_now(17, 2, None); - let addr = self - .tm_store - .lock() - .expect("Locking TM store failed") - .add_pus_tm(&ping_reply); + let addr = self.tm_store.add_pus_tm(&ping_reply); self.tm_tx .send(addr) .expect("Sending TM to TM funnel failed"); diff --git a/fsrc-example/src/bin/obsw/tmtc.rs b/fsrc-example/src/bin/obsw/tmtc.rs index 683569a..1c9ad9e 100644 --- a/fsrc-example/src/bin/obsw/tmtc.rs +++ b/fsrc-example/src/bin/obsw/tmtc.rs @@ -1,28 +1,28 @@ use fsrc_core::hal::host::udp_server::{ReceiveResult, UdpTcServer}; use std::net::SocketAddr; -use std::sync::{mpsc, Arc, Mutex}; +use std::sync::mpsc; use std::thread; use std::time::Duration; use crate::ccsds::CcsdsReceiver; use crate::pus::PusReceiver; use crate::UdpTmtcServer; -use fsrc_core::pool::{LocalPool, StoreAddr}; +use fsrc_core::pool::{SharedPool, StoreAddr}; +use fsrc_core::pus::verification::SharedStdVerifReporterWithSender; use fsrc_core::tmtc::{CcsdsDistributor, CcsdsError, PusDistributor}; use spacepackets::tm::PusTm; pub const PUS_APID: u16 = 0x02; +#[derive(Clone)] pub struct TmStore { - pub pool: LocalPool, + pub pool: SharedPool, } impl TmStore { pub fn add_pus_tm(&mut self, pus_tm: &PusTm) -> StoreAddr { - let (addr, buf) = self - .pool - .free_element(pus_tm.len_packed()) - .expect("Store error"); + let mut pg = self.pool.write().expect("Error locking TM store"); + let (addr, buf) = pg.free_element(pus_tm.len_packed()).expect("Store error"); pus_tm .write_to(buf) .expect("Writing PUS TM to store failed"); @@ -33,10 +33,16 @@ impl TmStore { pub fn core_tmtc_task( tm_creator_tx: mpsc::Sender, tm_server_rx: mpsc::Receiver, - tm_store: Arc>, + tm_store_helper: TmStore, addr: SocketAddr, + verif_reporter: SharedStdVerifReporterWithSender, ) { - let pus_receiver = PusReceiver::new(PUS_APID, tm_creator_tx, tm_store.clone()); + let pus_receiver = PusReceiver::new( + PUS_APID, + tm_creator_tx, + tm_store_helper.clone(), + verif_reporter, + ); let pus_distributor = PusDistributor::new(Box::new(pus_receiver)); let ccsds_receiver = CcsdsReceiver { pus_handler: pus_distributor, @@ -44,10 +50,11 @@ pub fn core_tmtc_task( let ccsds_distributor = CcsdsDistributor::new(Box::new(ccsds_receiver)); let udp_tc_server = UdpTcServer::new(addr, 2048, Box::new(ccsds_distributor)) .expect("Creating UDP TMTC server failed"); + let mut udp_tmtc_server = UdpTmtcServer { udp_tc_server, tm_rx: tm_server_rx, - tm_store, + tm_store: tm_store_helper.pool.clone(), }; loop { core_tmtc_loop(&mut udp_tmtc_server); @@ -89,9 +96,9 @@ fn core_tm_handling(udp_tmtc_server: &mut UdpTmtcServer, recv_addr: &SocketAddr) while let Ok(addr) = udp_tmtc_server.tm_rx.try_recv() { let mut store_lock = udp_tmtc_server .tm_store - .lock() + .write() .expect("Locking TM store failed"); - let pg = store_lock.pool.read_with_guard(addr); + let pg = store_lock.read_with_guard(addr); let buf = pg.read().expect("Error reading TM pool data"); println!("Sending TM"); udp_tmtc_server diff --git a/fsrc-example/src/bin/test.rs b/fsrc-example/src/bin/test.rs index 026ccfc..a979878 100644 --- a/fsrc-example/src/bin/test.rs +++ b/fsrc-example/src/bin/test.rs @@ -27,10 +27,7 @@ impl FieldDataProvider for FixedFieldDataWrapper { type FieldDataTraitObj = Box; fn main() { - let (s0, r0): ( - Sender, - Receiver, - ) = bounded(5); + let (s0, r0): (Sender, Receiver) = bounded(5); let data_wrapper = FixedFieldDataWrapper::from_two_u32(2, 3); s0.send(Box::new(data_wrapper)).unwrap(); let jh0 = thread::spawn(move || { From 65c423bdd2d8dc40da9af0003037e953a3e226fa Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 10 Sep 2022 14:10:18 +0200 Subject: [PATCH 32/43] add code sample --- fsrc-example/src/bin/test2.rs | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 fsrc-example/src/bin/test2.rs diff --git a/fsrc-example/src/bin/test2.rs b/fsrc-example/src/bin/test2.rs new file mode 100644 index 0000000..6e59cd9 --- /dev/null +++ b/fsrc-example/src/bin/test2.rs @@ -0,0 +1,54 @@ +use std::sync::{Arc, Mutex}; + +trait ProvidesFoo { + fn magic(&mut self); +} + +struct Foo { + magic_value: u32 +} + +impl Default for Foo { + fn default() -> Self { + Self {magic_value: 42} + } +} + +impl ProvidesFoo for Foo { + fn magic(&mut self) { + println!("ProvidesFoo magic"); + } +} + +pub type SharedFooConcrete = Arc>>; +pub type SharedFooTraitObj = Arc>>; + +struct FooProvider { + foo_as_trait_obj: SharedFooTraitObj + +} + +impl FooProvider { + fn magic_and_then_some(&mut self) { + let mut fooguard = self.foo_as_trait_obj.lock().unwrap(); + fooguard.magic(); + println!("Additional magic"); + } +} + +fn uses_shared_foo_boxed_trait_obj(foo: SharedFooTraitObj) { + let mut foo_provider = FooProvider { + foo_as_trait_obj: foo + }; + foo_provider.magic_and_then_some(); +} +fn uses_shared_foo_concrete(foo: SharedFooConcrete) { + let mut fooguard = foo.lock().unwrap(); + fooguard.magic(); +} + +fn main() { + let shared_foo = Arc::new(Mutex::new(Box::new(Foo::default()))); + uses_shared_foo_concrete(shared_foo.clone()); + uses_shared_foo_boxed_trait_obj(shared_foo); +} From f87ff7523f7579897bdfe6f6adce78126d80bc0e Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 10 Sep 2022 19:57:58 +0200 Subject: [PATCH 33/43] add some From Impls --- fsrc-core/src/pus/verification.rs | 31 ++++++--- fsrc-example/src/bin/test2.rs | 105 +++++++++++++++--------------- 2 files changed, 75 insertions(+), 61 deletions(-) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index e53d5c6..f00fc02 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -166,6 +166,12 @@ pub enum VerificationError { PusError(PusError), } +impl From for VerificationError { + fn from(e: ByteConversionError) -> Self { + VerificationError::ByteConversionError(e) + } +} + /// If a verification operation fails, the passed token will be returned as well. This allows /// re-trying the operation at a later point. #[derive(Debug, Clone)] @@ -556,12 +562,9 @@ impl VerificationReporter { .unwrap(); idx += step.byte_width() as usize; } - params - .failure_code - .to_bytes( - &mut self.source_data_buf[idx..idx + params.failure_code.byte_width() as usize], - ) - .map_err(|e| VerificationError::::ByteConversionError(e))?; + params.failure_code.to_bytes( + &mut self.source_data_buf[idx..idx + params.failure_code.byte_width() as usize], + )?; idx += params.failure_code.byte_width() as usize; if let Some(failure_data) = params.failure_data { self.source_data_buf[idx..idx + failure_data.len()].copy_from_slice(failure_data); @@ -732,6 +735,18 @@ mod stdmod { RxDisconnected(StoreAddr), } + impl From for StdVerifSenderError { + fn from(e: StoreError) -> Self { + StdVerifSenderError::StoreError(e) + } + } + + impl From for VerificationError { + fn from(e: StoreError) -> Self { + VerificationError::SendError(e.into()) + } + } + trait SendBackend: Send { fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr>; } @@ -824,9 +839,7 @@ mod stdmod { tm: PusTm, ) -> Result<(), VerificationError> { let operation = |mut mg: RwLockWriteGuard| { - let (addr, buf) = mg.free_element(tm.len_packed()).map_err(|e| { - VerificationError::SendError(StdVerifSenderError::StoreError(e)) - })?; + let (addr, buf) = mg.free_element(tm.len_packed())?; tm.write_to(buf).map_err(VerificationError::PusError)?; drop(mg); self.tx.send(addr).map_err(|_| { diff --git a/fsrc-example/src/bin/test2.rs b/fsrc-example/src/bin/test2.rs index 6e59cd9..3121c51 100644 --- a/fsrc-example/src/bin/test2.rs +++ b/fsrc-example/src/bin/test2.rs @@ -1,54 +1,55 @@ -use std::sync::{Arc, Mutex}; - -trait ProvidesFoo { - fn magic(&mut self); -} - -struct Foo { - magic_value: u32 -} - -impl Default for Foo { - fn default() -> Self { - Self {magic_value: 42} - } -} - -impl ProvidesFoo for Foo { - fn magic(&mut self) { - println!("ProvidesFoo magic"); - } -} - -pub type SharedFooConcrete = Arc>>; -pub type SharedFooTraitObj = Arc>>; - -struct FooProvider { - foo_as_trait_obj: SharedFooTraitObj - -} - -impl FooProvider { - fn magic_and_then_some(&mut self) { - let mut fooguard = self.foo_as_trait_obj.lock().unwrap(); - fooguard.magic(); - println!("Additional magic"); - } -} - -fn uses_shared_foo_boxed_trait_obj(foo: SharedFooTraitObj) { - let mut foo_provider = FooProvider { - foo_as_trait_obj: foo - }; - foo_provider.magic_and_then_some(); -} -fn uses_shared_foo_concrete(foo: SharedFooConcrete) { - let mut fooguard = foo.lock().unwrap(); - fooguard.magic(); -} - +// use std::sync::{Arc, Mutex}; +// +// trait ProvidesFoo { +// fn magic(&mut self); +// } +// +// struct Foo { +// magic_value: u32, +// } +// +// impl Default for Foo { +// fn default() -> Self { +// Self { magic_value: 42 } +// } +// } +// +// impl ProvidesFoo for Foo { +// fn magic(&mut self) { +// println!("ProvidesFoo magic {}", self.magic_value); +// } +// } +// +// type SharedFooConcrete = Arc>>; +// type SharedFooTraitObj = Arc>>; +// +// #[allow(dead_code)] +// struct FooProvider { +// foo_as_trait_obj: SharedFooTraitObj, +// } +// +// impl FooProvider { +// fn magic_and_then_some(&mut self) { +// let mut fooguard = self.foo_as_trait_obj.lock().unwrap(); +// fooguard.magic(); +// println!("Additional magic"); +// } +// } +// +// #[allow(dead_code)] +// fn uses_shared_foo_boxed_trait_obj(foo: SharedFooTraitObj) { +// let mut foo_provider = FooProvider { +// foo_as_trait_obj: foo, +// }; +// foo_provider.magic_and_then_some(); +// } +// fn uses_shared_foo_concrete(foo: SharedFooConcrete) { +// let mut fooguard = foo.lock().unwrap(); +// fooguard.magic(); +// } +// fn main() { - let shared_foo = Arc::new(Mutex::new(Box::new(Foo::default()))); - uses_shared_foo_concrete(shared_foo.clone()); - uses_shared_foo_boxed_trait_obj(shared_foo); +// let shared_foo = Arc::new(Mutex::new(Box::new(Foo::default()))); +// uses_shared_foo_concrete(shared_foo.clone()); +// // uses_shared_foo_boxed_trait_obj(shared_foo); } From f81775a8238ea72bcc7ddff898c84fdf8329ba81 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 10 Sep 2022 20:02:11 +0200 Subject: [PATCH 34/43] cargo fmt --- fsrc-example/src/bin/test2.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fsrc-example/src/bin/test2.rs b/fsrc-example/src/bin/test2.rs index 3121c51..2992223 100644 --- a/fsrc-example/src/bin/test2.rs +++ b/fsrc-example/src/bin/test2.rs @@ -49,7 +49,7 @@ // } // fn main() { -// let shared_foo = Arc::new(Mutex::new(Box::new(Foo::default()))); -// uses_shared_foo_concrete(shared_foo.clone()); -// // uses_shared_foo_boxed_trait_obj(shared_foo); + // let shared_foo = Arc::new(Mutex::new(Box::new(Foo::default()))); + // uses_shared_foo_concrete(shared_foo.clone()); + // // uses_shared_foo_boxed_trait_obj(shared_foo); } From a261f15589ce54e0da00b53c125ef5d56eba9428 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sat, 10 Sep 2022 20:18:19 +0200 Subject: [PATCH 35/43] add acceptances handling --- fsrc-example/src/bin/obsw/pus.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fsrc-example/src/bin/obsw/pus.rs b/fsrc-example/src/bin/obsw/pus.rs index eb76d09..18a663f 100644 --- a/fsrc-example/src/bin/obsw/pus.rs +++ b/fsrc-example/src/bin/obsw/pus.rs @@ -4,6 +4,7 @@ use fsrc_core::pus::verification::SharedStdVerifReporterWithSender; use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper; use fsrc_core::tmtc::PusServiceProvider; use spacepackets::tc::{PusTc, PusTcSecondaryHeaderT}; +use spacepackets::time::{CdsShortTimeProvider, TimeWriter}; use spacepackets::SpHeader; use std::sync::mpsc; @@ -12,6 +13,8 @@ pub struct PusReceiver { pub tm_tx: mpsc::Sender, pub tm_store: TmStore, pub verif_reporter: SharedStdVerifReporterWithSender, + stamper: CdsShortTimeProvider, + time_stamp: [u8; 7], } impl PusReceiver { @@ -26,6 +29,8 @@ impl PusReceiver { tm_tx, tm_store, verif_reporter, + stamper: CdsShortTimeProvider::default(), + time_stamp: [0; 7], } } } @@ -39,6 +44,21 @@ impl PusServiceProvider for PusReceiver { _header: &SpHeader, pus_tc: &PusTc, ) -> Result<(), Self::Error> { + let mut reporter = self + .verif_reporter + .lock() + .expect("Locking Verification Reporter failed"); + let init_token = reporter.add_tc(pus_tc); + self.stamper + .update_from_now() + .expect("Updating time for time stamp failed"); + self.stamper + .write_to_bytes(&mut self.time_stamp) + .expect("Writing time stamp failed"); + let _accepted_token = reporter + .acceptance_success(init_token, &self.time_stamp) + .expect("Acceptance success failure"); + drop(reporter); if service == 17 { self.handle_test_service(pus_tc); } From ab2fa4c050eebefae9ff20fd86e9e01efd15c7a5 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 11 Sep 2022 14:51:25 +0200 Subject: [PATCH 36/43] verification reply handling working --- fsrc-core/src/pus/verification.rs | 7 ++++ fsrc-example/src/bin/client.rs | 69 ++++++++++++++++++++++++------- fsrc-example/src/bin/obsw/pus.rs | 20 +++++++-- fsrc-example/src/bin/test2.rs | 55 ------------------------ 4 files changed, 78 insertions(+), 73 deletions(-) delete mode 100644 fsrc-example/src/bin/test2.rs diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index f00fc02..a011ecb 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -74,6 +74,7 @@ use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; +use core::fmt::{Display, Formatter}; use core::hash::{Hash, Hasher}; use core::marker::PhantomData; use core::mem::size_of; @@ -101,6 +102,12 @@ pub struct RequestId { psc: PacketSequenceCtrl, } +impl Display for RequestId { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "Request ID {:#08x}", self.raw()) + } +} + impl Hash for RequestId { fn hash(&self, state: &mut H) { self.raw().hash(state); diff --git a/fsrc-example/src/bin/client.rs b/fsrc-example/src/bin/client.rs index 8523e9e..0411911 100644 --- a/fsrc-example/src/bin/client.rs +++ b/fsrc-example/src/bin/client.rs @@ -1,3 +1,4 @@ +use fsrc_core::pus::verification::RequestId; use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT}; use spacepackets::ecss::PusPacket; use spacepackets::tc::PusTc; @@ -8,11 +9,16 @@ use std::time::Duration; fn main() { let mut buf = [0; 32]; - println!("Packing and sending PUS ping command TC[17,1]"); + let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); let mut sph = SpHeader::tc(0x02, 0, 0).unwrap(); let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed"); + let tc_req_id = RequestId::new(&pus_tc); + println!( + "Packing and sending PUS ping command TC[17,1] with {}", + tc_req_id + ); let size = pus_tc.write_to(&mut buf).expect("Creating PUS TC failed"); client .send_to(&buf[0..size], &addr) @@ -20,19 +26,54 @@ fn main() { client .set_read_timeout(Some(Duration::from_secs(2))) .expect("Setting read timeout failed"); - if let Ok(_len) = client.recv(&mut buf) { - let (pus_tm, size) = PusTm::new_from_raw_slice(&buf, 7).expect("Parsing PUS TM failed"); - if pus_tm.service() == 17 && pus_tm.subservice() == 2 { - println!("Received PUS Ping Reply TM[17,2]") - } else { - println!( - "Received TM[{}, {}] with {} bytes", - pus_tm.service(), - pus_tm.subservice(), - size - ); + loop { + let res = client.recv(&mut buf); + match res { + Ok(_len) => { + let (pus_tm, size) = + PusTm::new_from_raw_slice(&buf, 7).expect("Parsing PUS TM failed"); + if pus_tm.service() == 17 && pus_tm.subservice() == 2 { + println!("Received PUS Ping Reply TM[17,2]") + } else if pus_tm.service() == 1 { + if pus_tm.source_data().is_none() { + println!("Invalid verification TM, no source data"); + } + let src_data = pus_tm.source_data().unwrap(); + if src_data.len() < 4 { + println!("Invalid verification TM source data, less than 4 bytes") + } + let req_id = RequestId::from_bytes(src_data).unwrap(); + if pus_tm.subservice() == 1 { + println!("Received TM[1,1] acceptance success for {}", req_id) + } else if pus_tm.subservice() == 3 { + println!("Received TM[1,3] start success for {}", req_id) + } else if pus_tm.subservice() == 5 { + println!("Received TM[1,5] step success for {}", req_id) + } else if pus_tm.subservice() == 7 { + println!( + "Received TM[1,7] completion success for request ID {}", + req_id + ) + } + } else { + println!( + "Received TM[{}, {}] with {} bytes", + pus_tm.service(), + pus_tm.subservice(), + size + ); + } + } + Err(ref e) + if e.kind() == std::io::ErrorKind::WouldBlock + || e.kind() == std::io::ErrorKind::TimedOut => + { + println!("No reply received for 2 seconds"); + break; + } + _ => { + println!("UDP receive error {:?}", res.unwrap_err()); + } } - } else { - println!("No reply received for 2 seconds or timeout"); } } diff --git a/fsrc-example/src/bin/obsw/pus.rs b/fsrc-example/src/bin/obsw/pus.rs index 18a663f..f4c59e3 100644 --- a/fsrc-example/src/bin/obsw/pus.rs +++ b/fsrc-example/src/bin/obsw/pus.rs @@ -1,6 +1,8 @@ use crate::tmtc::TmStore; use fsrc_core::pool::StoreAddr; -use fsrc_core::pus::verification::SharedStdVerifReporterWithSender; +use fsrc_core::pus::verification::{ + SharedStdVerifReporterWithSender, StateAccepted, VerificationToken, +}; use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper; use fsrc_core::tmtc::PusServiceProvider; use spacepackets::tc::{PusTc, PusTcSecondaryHeaderT}; @@ -55,27 +57,37 @@ impl PusServiceProvider for PusReceiver { self.stamper .write_to_bytes(&mut self.time_stamp) .expect("Writing time stamp failed"); - let _accepted_token = reporter + let accepted_token = reporter .acceptance_success(init_token, &self.time_stamp) .expect("Acceptance success failure"); drop(reporter); if service == 17 { - self.handle_test_service(pus_tc); + self.handle_test_service(pus_tc, accepted_token); } Ok(()) } } impl PusReceiver { - fn handle_test_service(&mut self, pus_tc: &PusTc) { + fn handle_test_service(&mut self, pus_tc: &PusTc, token: VerificationToken) { if pus_tc.subservice() == 1 { println!("Received PUS ping command TC[17,1]"); println!("Sending ping reply PUS TM[17,2]"); let ping_reply = self.tm_helper.create_pus_tm_timestamp_now(17, 2, None); let addr = self.tm_store.add_pus_tm(&ping_reply); + let mut reporter = self + .verif_reporter + .lock() + .expect("Error locking verification reporter"); + let start_token = reporter + .start_success(token, &self.time_stamp) + .expect("Error sending start success"); self.tm_tx .send(addr) .expect("Sending TM to TM funnel failed"); + reporter + .completion_success(start_token, &self.time_stamp) + .expect("Error sending completion success"); } } } diff --git a/fsrc-example/src/bin/test2.rs b/fsrc-example/src/bin/test2.rs deleted file mode 100644 index 2992223..0000000 --- a/fsrc-example/src/bin/test2.rs +++ /dev/null @@ -1,55 +0,0 @@ -// use std::sync::{Arc, Mutex}; -// -// trait ProvidesFoo { -// fn magic(&mut self); -// } -// -// struct Foo { -// magic_value: u32, -// } -// -// impl Default for Foo { -// fn default() -> Self { -// Self { magic_value: 42 } -// } -// } -// -// impl ProvidesFoo for Foo { -// fn magic(&mut self) { -// println!("ProvidesFoo magic {}", self.magic_value); -// } -// } -// -// type SharedFooConcrete = Arc>>; -// type SharedFooTraitObj = Arc>>; -// -// #[allow(dead_code)] -// struct FooProvider { -// foo_as_trait_obj: SharedFooTraitObj, -// } -// -// impl FooProvider { -// fn magic_and_then_some(&mut self) { -// let mut fooguard = self.foo_as_trait_obj.lock().unwrap(); -// fooguard.magic(); -// println!("Additional magic"); -// } -// } -// -// #[allow(dead_code)] -// fn uses_shared_foo_boxed_trait_obj(foo: SharedFooTraitObj) { -// let mut foo_provider = FooProvider { -// foo_as_trait_obj: foo, -// }; -// foo_provider.magic_and_then_some(); -// } -// fn uses_shared_foo_concrete(foo: SharedFooConcrete) { -// let mut fooguard = foo.lock().unwrap(); -// fooguard.magic(); -// } -// -fn main() { - // let shared_foo = Arc::new(Mutex::new(Box::new(Foo::default()))); - // uses_shared_foo_concrete(shared_foo.clone()); - // // uses_shared_foo_boxed_trait_obj(shared_foo); -} From 35942aadb66550ed58a199e2a31deec011544921 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 11 Sep 2022 14:58:51 +0200 Subject: [PATCH 37/43] this should suffice --- fsrc-core/src/pus/verification.rs | 2 +- fsrc-example/src/bin/client.rs | 26 +++++++++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index a011ecb..08d4cd7 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -104,7 +104,7 @@ pub struct RequestId { impl Display for RequestId { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "Request ID {:#08x}", self.raw()) + write!(f, "{:#08x}", self.raw()) } } diff --git a/fsrc-example/src/bin/client.rs b/fsrc-example/src/bin/client.rs index 0411911..1f56418 100644 --- a/fsrc-example/src/bin/client.rs +++ b/fsrc-example/src/bin/client.rs @@ -9,14 +9,13 @@ use std::time::Duration; fn main() { let mut buf = [0; 32]; - let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); let mut sph = SpHeader::tc(0x02, 0, 0).unwrap(); let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); let client = UdpSocket::bind("127.0.0.1:7302").expect("Connecting to UDP server failed"); let tc_req_id = RequestId::new(&pus_tc); println!( - "Packing and sending PUS ping command TC[17,1] with {}", + "Packing and sending PUS ping command TC[17,1] with request ID {}", tc_req_id ); let size = pus_tc.write_to(&mut buf).expect("Creating PUS TC failed"); @@ -44,16 +43,33 @@ fn main() { } let req_id = RequestId::from_bytes(src_data).unwrap(); if pus_tm.subservice() == 1 { - println!("Received TM[1,1] acceptance success for {}", req_id) + println!( + "Received TM[1,1] acceptance success for request ID {}", + req_id + ) + } else if pus_tm.subservice() == 2 { + println!( + "Received TM[1,2] acceptance failure for request ID {}", + req_id + ) } else if pus_tm.subservice() == 3 { - println!("Received TM[1,3] start success for {}", req_id) + println!("Received TM[1,3] start success for request ID {}", req_id) + } else if pus_tm.subservice() == 4 { + println!("Received TM[1,2] start failure for request ID {}", req_id) } else if pus_tm.subservice() == 5 { - println!("Received TM[1,5] step success for {}", req_id) + println!("Received TM[1,5] step success for request ID {}", req_id) + } else if pus_tm.subservice() == 6 { + println!("Received TM[1,6] step failure for request ID {}", req_id) } else if pus_tm.subservice() == 7 { println!( "Received TM[1,7] completion success for request ID {}", req_id ) + } else if pus_tm.subservice() == 8 { + println!( + "Received TM[1,8] completion failure for request ID {}", + req_id + ); } } else { println!( From 13f4852c894d164616bb0c1eaa6be814ca62b751 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 11 Sep 2022 15:23:11 +0200 Subject: [PATCH 38/43] add docs --- fsrc-core/src/hal/host/mod.rs | 1 + fsrc-core/src/hal/host/udp_server.rs | 36 +++++++++++++++++++++------- fsrc-example/src/bin/obsw/tmtc.rs | 2 +- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/fsrc-core/src/hal/host/mod.rs b/fsrc-core/src/hal/host/mod.rs index d9e7799..8057db1 100644 --- a/fsrc-core/src/hal/host/mod.rs +++ b/fsrc-core/src/hal/host/mod.rs @@ -1 +1,2 @@ +//! Helper modules intended to be used on hosts with a full [std] runtime pub mod udp_server; diff --git a/fsrc-core/src/hal/host/udp_server.rs b/fsrc-core/src/hal/host/udp_server.rs index 1414901..e880b5f 100644 --- a/fsrc-core/src/hal/host/udp_server.rs +++ b/fsrc-core/src/hal/host/udp_server.rs @@ -1,10 +1,25 @@ +//! UDP server helper components use crate::tmtc::ReceivesTc; use std::boxed::Box; -use std::io::ErrorKind; +use std::io::{Error, ErrorKind}; use std::net::{SocketAddr, ToSocketAddrs, UdpSocket}; use std::vec; use std::vec::Vec; +/// This TC server helper can be used to receive raw PUS telecommands thorough a UDP interface. +/// +/// It caches all received telecomands into a vector. The maximum expected telecommand size should +/// be declared upfront. This avoids dynamic allocation during run-time. The user can specify a TC +/// receiver in form of a special trait object which implements [ReceivesTc]. Please note that the +/// receiver should copy out the received data if it the data is required past the +/// [ReceivesTc::pass_tc] call. +/// +/// # Examples +/// +/// The [fsrc-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/obsw-client-example/fsrc-example) server code includes +/// [example code](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/obsw-client-example/fsrc-example/src/bin/obsw/tmtc.rs) +/// on how to use this TC server. It uses the server to receive PUS telecommands on a specific port +/// and then forwards them to a generic CCSDS packet receiver. pub struct UdpTcServer { pub socket: UdpSocket, recv_buf: Vec, @@ -15,26 +30,29 @@ pub struct UdpTcServer { #[derive(Debug)] pub enum ReceiveResult { WouldBlock, - OtherIoError(std::io::Error), + IoError(Error), ReceiverError(E), } +impl From for ReceiveResult { + fn from(e: Error) -> Self { + ReceiveResult::IoError(e) + } +} + impl UdpTcServer { pub fn new( addr: A, max_recv_size: usize, tc_receiver: Box>, - ) -> Result { + ) -> Result { let server = Self { socket: UdpSocket::bind(addr)?, recv_buf: vec![0; max_recv_size], sender_addr: None, tc_receiver, }; - server - .socket - .set_nonblocking(true) - .expect("Setting server non blocking failed"); + server.socket.set_nonblocking(true)?; Ok(server) } @@ -42,10 +60,10 @@ impl UdpTcServer { let res = match self.socket.recv_from(&mut self.recv_buf) { Ok(res) => res, Err(e) => { - return if e.kind() == ErrorKind::WouldBlock { + return if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut { Err(ReceiveResult::WouldBlock) } else { - Err(ReceiveResult::OtherIoError(e)) + Err(e.into()) } } }; diff --git a/fsrc-example/src/bin/obsw/tmtc.rs b/fsrc-example/src/bin/obsw/tmtc.rs index 1c9ad9e..4ac7237 100644 --- a/fsrc-example/src/bin/obsw/tmtc.rs +++ b/fsrc-example/src/bin/obsw/tmtc.rs @@ -83,7 +83,7 @@ fn core_tc_handling(udp_tmtc_server: &mut UdpTmtcServer) -> bool { true } }, - ReceiveResult::OtherIoError(e) => { + ReceiveResult::IoError(e) => { println!("IO error {e}"); false } From fe709826690cb11e9d2a4b527583ab93a72517c2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 11 Sep 2022 16:20:30 +0200 Subject: [PATCH 39/43] unittests for UDP TC server --- fsrc-core/src/hal/host/udp_server.rs | 94 ++++++++++++++++++++++++++-- fsrc-core/src/hal/mod.rs | 1 + fsrc-core/src/tmtc/mod.rs | 5 +- fsrc-example/src/bin/obsw/tmtc.rs | 2 +- 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/fsrc-core/src/hal/host/udp_server.rs b/fsrc-core/src/hal/host/udp_server.rs index e880b5f..f112ea7 100644 --- a/fsrc-core/src/hal/host/udp_server.rs +++ b/fsrc-core/src/hal/host/udp_server.rs @@ -16,8 +16,8 @@ use std::vec::Vec; /// /// # Examples /// -/// The [fsrc-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/obsw-client-example/fsrc-example) server code includes -/// [example code](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/obsw-client-example/fsrc-example/src/bin/obsw/tmtc.rs) +/// The [fsrc-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example) server code includes +/// [example code](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example/src/bin/obsw/tmtc.rs) /// on how to use this TC server. It uses the server to receive PUS telecommands on a specific port /// and then forwards them to a generic CCSDS packet receiver. pub struct UdpTcServer { @@ -29,7 +29,7 @@ pub struct UdpTcServer { #[derive(Debug)] pub enum ReceiveResult { - WouldBlock, + NothingReceived, IoError(Error), ReceiverError(E), } @@ -40,7 +40,21 @@ impl From for ReceiveResult { } } -impl UdpTcServer { +impl PartialEq for ReceiveResult { + fn eq(&self, other: &Self) -> bool { + use ReceiveResult::*; + match (self, other) { + (IoError(ref e), IoError(ref other_e)) => e.kind() == other_e.kind(), + (NothingReceived, NothingReceived) => true, + (ReceiverError(e), ReceiverError(other_e)) => e == other_e, + _ => false, + } + } +} + +impl Eq for ReceiveResult {} + +impl UdpTcServer { pub fn new( addr: A, max_recv_size: usize, @@ -61,7 +75,7 @@ impl UdpTcServer { Ok(res) => res, Err(e) => { return if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut { - Err(ReceiveResult::WouldBlock) + Err(ReceiveResult::NothingReceived) } else { Err(e.into()) } @@ -79,3 +93,73 @@ impl UdpTcServer { self.sender_addr } } + +#[cfg(test)] +mod tests { + use crate::hal::host::udp_server::{ReceiveResult, UdpTcServer}; + use crate::tmtc::ReceivesTc; + use spacepackets::tc::PusTc; + use spacepackets::SpHeader; + use std::boxed::Box; + use std::collections::VecDeque; + use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; + use std::vec::Vec; + + #[derive(Default)] + struct PingReceiver { + pub sent_cmds: VecDeque>, + } + + impl ReceivesTc for PingReceiver { + type Error = (); + + fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { + let mut sent_data = Vec::new(); + sent_data.extend_from_slice(tc_raw); + self.sent_cmds.push_back(sent_data); + Ok(()) + } + } + + #[test] + fn basic_test() { + let mut buf = [0; 32]; + let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777); + let ping_receiver = PingReceiver::default(); + let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) + .expect("Creating UDP TMTC server failed"); + let mut sph = SpHeader::tc(0x02, 0, 0).unwrap(); + let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); + let len = pus_tc + .write_to(&mut buf) + .expect("Error writing PUS TC packet"); + let client = UdpSocket::bind("127.0.0.1:7778").expect("Connecting to UDP server failed"); + client + .send_to(&buf[0..len], dest_addr) + .expect("Error sending PUS TC via UDP"); + let local_addr = client.local_addr().unwrap(); + udp_tc_server + .try_recv_tc() + .expect("Error receiving sent telecommand"); + assert_eq!( + udp_tc_server.last_sender().expect("No sender set"), + local_addr + ); + let ping_receiver: &mut PingReceiver = udp_tc_server.tc_receiver.downcast_mut().unwrap(); + assert_eq!(ping_receiver.sent_cmds.len(), 1); + let sent_cmd = ping_receiver.sent_cmds.pop_front().unwrap(); + assert_eq!(sent_cmd, buf[0..len]); + } + + #[test] + fn test_nothing_received() { + let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7779); + let ping_receiver = PingReceiver::default(); + let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) + .expect("Creating UDP TMTC server failed"); + let res = udp_tc_server.try_recv_tc(); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err, ReceiveResult::NothingReceived); + } +} diff --git a/fsrc-core/src/hal/mod.rs b/fsrc-core/src/hal/mod.rs index 0c9616d..ddaf8f3 100644 --- a/fsrc-core/src/hal/mod.rs +++ b/fsrc-core/src/hal/mod.rs @@ -1,2 +1,3 @@ +//! # Hardware Abstraction Layer module #[cfg(feature = "std")] pub mod host; diff --git a/fsrc-core/src/tmtc/mod.rs b/fsrc-core/src/tmtc/mod.rs index d9e954a..b1d0b3d 100644 --- a/fsrc-core/src/tmtc/mod.rs +++ b/fsrc-core/src/tmtc/mod.rs @@ -6,6 +6,7 @@ //! Application Process ID (APID) or the ECSS PUS service type. This allows for fast packet //! routing without the overhead and complication of using message queues. However, it also requires use crate::error::{FsrcErrorRaw, FsrcGroupIds}; +use downcast_rs::{impl_downcast, Downcast}; use spacepackets::tc::PusTc; use spacepackets::SpHeader; @@ -43,11 +44,13 @@ const _FROM_BYTES_ZEROCOPY_ERROR: FsrcErrorRaw = FsrcErrorRaw::new( /// This trait is implemented by both the [crate::tmtc::pus_distrib::PusDistributor] and the /// [crate::tmtc::ccsds_distrib::CcsdsDistributor] which allows to pass the respective packets in /// raw byte format into them. -pub trait ReceivesTc { +pub trait ReceivesTc: Downcast { type Error; fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error>; } +impl_downcast!(ReceivesTc assoc Error); + /// Generic trait for object which can receive CCSDS space packets, for fsrc-example ECSS PUS packets /// for CCSDS File Delivery Protocol (CFDP) packets. /// diff --git a/fsrc-example/src/bin/obsw/tmtc.rs b/fsrc-example/src/bin/obsw/tmtc.rs index 4ac7237..5a34d1e 100644 --- a/fsrc-example/src/bin/obsw/tmtc.rs +++ b/fsrc-example/src/bin/obsw/tmtc.rs @@ -87,7 +87,7 @@ fn core_tc_handling(udp_tmtc_server: &mut UdpTmtcServer) -> bool { println!("IO error {e}"); false } - ReceiveResult::WouldBlock => false, + ReceiveResult::NothingReceived => false, }, } } From 0511a919975040284ed11a774812aa014cc81004 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 11 Sep 2022 16:28:16 +0200 Subject: [PATCH 40/43] added doctest --- fsrc-core/src/hal/host/udp_server.rs | 39 ++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/fsrc-core/src/hal/host/udp_server.rs b/fsrc-core/src/hal/host/udp_server.rs index f112ea7..2c7cd37 100644 --- a/fsrc-core/src/hal/host/udp_server.rs +++ b/fsrc-core/src/hal/host/udp_server.rs @@ -16,7 +16,42 @@ use std::vec::Vec; /// /// # Examples /// -/// The [fsrc-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example) server code includes +/// ``` +/// use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; +/// use fsrc_core::hal::host::udp_server::UdpTcServer; +/// use fsrc_core::tmtc::ReceivesTc; +/// use spacepackets::SpHeader; +/// use spacepackets::tc::PusTc; +/// +/// #[derive (Default)] +/// struct PingReceiver {} +/// impl ReceivesTc for PingReceiver { +/// type Error = (); +/// fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { +/// assert_eq!(tc_raw.len(), 13); +/// Ok(()) +/// } +/// } +/// +/// let mut buf = [0; 32]; +/// let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777); +/// let ping_receiver = PingReceiver::default(); +/// let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) +/// .expect("Creating UDP TMTC server failed"); +/// let mut sph = SpHeader::tc(0x02, 0, 0).unwrap(); +/// let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); +/// let len = pus_tc +/// .write_to(&mut buf) +/// .expect("Error writing PUS TC packet"); +/// assert_eq!(len, 13); +/// let client = UdpSocket::bind("127.0.0.1:7778").expect("Connecting to UDP server failed"); +/// client +/// .send_to(&buf[0..len], dest_addr) +/// .expect("Error sending PUS TC via UDP"); +/// ``` +/// +/// The [fsrc-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example) +/// server code also includes /// [example code](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example/src/bin/obsw/tmtc.rs) /// on how to use this TC server. It uses the server to receive PUS telecommands on a specific port /// and then forwards them to a generic CCSDS packet receiver. @@ -40,7 +75,7 @@ impl From for ReceiveResult { } } -impl PartialEq for ReceiveResult { +impl PartialEq for ReceiveResult { fn eq(&self, other: &Self) -> bool { use ReceiveResult::*; match (self, other) { From 5b7e18a8604e4664ca185c1b331566301c6c157f Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 11 Sep 2022 16:30:47 +0200 Subject: [PATCH 41/43] some doc typos --- fsrc-core/src/pool.rs | 2 +- fsrc-core/src/pus/verification.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fsrc-core/src/pool.rs b/fsrc-core/src/pool.rs index 66b68a5..8411a35 100644 --- a/fsrc-core/src/pool.rs +++ b/fsrc-core/src/pool.rs @@ -6,7 +6,7 @@ //! embedded environments. The pool implementation will also track the size of the data stored //! inside it. //! -//! Transaction with the [pool][LocalPool] are done using a special [address][StoreAddr] type. +//! Transactions with the [pool][LocalPool] are done using a special [address][StoreAddr] type. //! Adding any data to the pool will yield a store address. Modification and read operations are //! done using a reference to a store address. Deletion will consume the store address. //! diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 08d4cd7..095649d 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -10,7 +10,7 @@ //! # Examples //! //! Basic single-threaded example where a full success sequence for a given ping telecommand is -//! executed. Note that the verification port could also be done in a separate thread. +//! executed. Note that the verification part could also be done in a separate thread. //! //! ``` //! use std::sync::{Arc, RwLock}; From 877c79752c029d31b7156954052e5b5bd5b6d29c Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 11 Sep 2022 16:31:38 +0200 Subject: [PATCH 42/43] link correction --- fsrc-core/src/pus/verification.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 095649d..4af6ad6 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -68,7 +68,7 @@ //! } //! ``` //! -//! The [integration test](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/obsw-client-example/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 //! context involving multiple threads use alloc::boxed::Box; From cb4c23ad8479e33a70c113288958fa93899d055e Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 11 Sep 2022 16:32:17 +0200 Subject: [PATCH 43/43] remove unused dependency --- Cargo.lock | 21 --------------------- fsrc-core/Cargo.toml | 1 - 2 files changed, 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d3a336..0e51f1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,7 +243,6 @@ dependencies = [ "postcard", "serde", "spacepackets", - "thiserror", "zerocopy", ] @@ -630,26 +629,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "thiserror" -version = "1.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-ident" version = "1.0.3" diff --git a/fsrc-core/Cargo.toml b/fsrc-core/Cargo.toml index 482f2ab..8188273 100644 --- a/fsrc-core/Cargo.toml +++ b/fsrc-core/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -thiserror = "1.0" delegate = "0.8.0" hashbrown = "0.12.3"