OBSW-Client Example #11
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -202,6 +202,17 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "downcast-rs"
|
name = "downcast-rs"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -224,7 +235,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bus",
|
"bus",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"delegate",
|
"delegate 0.8.0",
|
||||||
"downcast-rs",
|
"downcast-rs",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@ -241,6 +252,7 @@ name = "fsrc-example"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
"delegate 0.8.0",
|
||||||
"fsrc-core",
|
"fsrc-core",
|
||||||
"spacepackets",
|
"spacepackets",
|
||||||
]
|
]
|
||||||
@ -573,7 +585,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crc",
|
"crc",
|
||||||
"delegate",
|
"delegate 0.7.0",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"postcard",
|
"postcard",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
delegate = "0.7.0"
|
delegate = "0.8.0"
|
||||||
hashbrown = "0.12.3"
|
hashbrown = "0.12.3"
|
||||||
|
|
||||||
[dependencies.num-traits]
|
[dependencies.num-traits]
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
//! # Example
|
//! # 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
|
//! // 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)]);
|
//! 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;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::boxed::Box;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
type NumBlocks = u16;
|
type NumBlocks = u16;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub type ShareablePoolProvider = Box<dyn PoolProvider + Send + Sync>;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub type SharedPool = Arc<RwLock<ShareablePoolProvider>>;
|
||||||
|
|
||||||
/// Configuration structure of the [local pool][LocalPool]
|
/// Configuration structure of the [local pool][LocalPool]
|
||||||
///
|
///
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
@ -151,10 +160,52 @@ pub enum StoreError {
|
|||||||
InternalError(String),
|
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<StoreAddr, StoreError>;
|
||||||
|
|
||||||
|
/// 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<bool, StoreError>;
|
||||||
|
}
|
||||||
|
|
||||||
impl LocalPool {
|
impl LocalPool {
|
||||||
const STORE_FREE: PoolSize = PoolSize::MAX;
|
const STORE_FREE: PoolSize = PoolSize::MAX;
|
||||||
const MAX_SIZE: PoolSize = Self::STORE_FREE - 1;
|
const MAX_SIZE: PoolSize = Self::STORE_FREE - 1;
|
||||||
|
|
||||||
/// Create a new local pool from the [given configuration][PoolCfg]. This function will sanitize
|
/// Create a new local pool from the [given configuration][PoolCfg]. This function will sanitize
|
||||||
/// the given configuration as well.
|
/// the given configuration as well.
|
||||||
pub fn new(mut cfg: PoolCfg) -> LocalPool {
|
pub fn new(mut cfg: PoolCfg) -> LocalPool {
|
||||||
@ -175,96 +226,6 @@ impl LocalPool {
|
|||||||
local_pool
|
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<StoreAddr, StoreError> {
|
|
||||||
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<bool, StoreError> {
|
|
||||||
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<usize, StoreError> {
|
fn addr_check(&self, addr: &StoreAddr) -> Result<usize, StoreError> {
|
||||||
self.validate_addr(addr)?;
|
self.validate_addr(addr)?;
|
||||||
let pool_idx = addr.pool_idx as usize;
|
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<StoreAddr, StoreError> {
|
||||||
|
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<bool, StoreError> {
|
||||||
|
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> {
|
pub struct PoolGuard<'a> {
|
||||||
pool: &'a mut LocalPool,
|
pool: &'a mut LocalPool,
|
||||||
pub addr: StoreAddr,
|
pub addr: StoreAddr,
|
||||||
@ -418,7 +446,8 @@ impl<'a> PoolRwGuard<'a> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::pool::{
|
use crate::pool::{
|
||||||
LocalPool, PoolCfg, PoolGuard, PoolRwGuard, StoreAddr, StoreError, StoreIdError,
|
LocalPool, PoolCfg, PoolGuard, PoolProvider, PoolRwGuard, StoreAddr, StoreError,
|
||||||
|
StoreIdError,
|
||||||
};
|
};
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! use std::sync::{Arc, RwLock};
|
//! use std::sync::{Arc, RwLock};
|
||||||
//! use std::time::Duration;
|
//! 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 fsrc_core::pus::verification::{CrossbeamVerifSender, VerificationReporterCfg, VerificationReporterWithSender};
|
||||||
//! use spacepackets::ecss::PusPacket;
|
//! use spacepackets::ecss::PusPacket;
|
||||||
//! use spacepackets::SpHeader;
|
//! use spacepackets::SpHeader;
|
||||||
@ -26,7 +26,7 @@
|
|||||||
//! const TEST_APID: u16 = 0x02;
|
//! const TEST_APID: u16 = 0x02;
|
||||||
//!
|
//!
|
||||||
//! let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
|
//! 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 (verif_tx, verif_rx) = crossbeam_channel::bounded(10);
|
||||||
//! let sender = CrossbeamVerifSender::new(shared_tm_pool.clone(), verif_tx);
|
//! let sender = CrossbeamVerifSender::new(shared_tm_pool.clone(), verif_tx);
|
||||||
//! let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8);
|
//! let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8);
|
||||||
@ -87,7 +87,10 @@ use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader};
|
|||||||
use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl};
|
use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[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 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.
|
/// This field equivalent to the first two bytes of the CCSDS space packet header.
|
||||||
@ -711,11 +714,16 @@ impl<E: 'static> VerificationReporterWithSender<E> {
|
|||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
mod stdmod {
|
mod stdmod {
|
||||||
use crate::pool::{LocalPool, StoreAddr, StoreError};
|
use crate::pool::{ShareablePoolProvider, SharedPool, StoreAddr, StoreError};
|
||||||
use crate::pus::verification::{VerificationError, VerificationSender};
|
use crate::pus::verification::{
|
||||||
|
VerificationError, VerificationReporterWithSender, VerificationSender,
|
||||||
|
};
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
use spacepackets::tm::PusTm;
|
use spacepackets::tm::PusTm;
|
||||||
use std::sync::{mpsc, Arc, RwLock, RwLockWriteGuard};
|
use std::sync::{mpsc, Arc, Mutex, RwLockWriteGuard};
|
||||||
|
|
||||||
|
pub type StdVerifReporterWithSender = VerificationReporterWithSender<StdVerifSenderError>;
|
||||||
|
pub type SharedStdVerifReporterWithSender = Arc<Mutex<StdVerifReporterWithSender>>;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum StdVerifSenderError {
|
pub enum StdVerifSenderError {
|
||||||
@ -730,12 +738,12 @@ mod stdmod {
|
|||||||
|
|
||||||
struct StdSenderBase<S> {
|
struct StdSenderBase<S> {
|
||||||
pub ignore_poison_error: bool,
|
pub ignore_poison_error: bool,
|
||||||
tm_store: Arc<RwLock<LocalPool>>,
|
tm_store: SharedPool,
|
||||||
tx: S,
|
tx: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: SendBackend> StdSenderBase<S> {
|
impl<S: SendBackend> StdSenderBase<S> {
|
||||||
pub fn new(tm_store: Arc<RwLock<LocalPool>>, tx: S) -> Self {
|
pub fn new(tm_store: SharedPool, tx: S) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ignore_poison_error: false,
|
ignore_poison_error: false,
|
||||||
tm_store,
|
tm_store,
|
||||||
@ -744,20 +752,23 @@ mod stdmod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl<S: Sync> Sync for StdSenderBase<S> {}
|
||||||
|
unsafe impl<S: Send> Send for StdSenderBase<S> {}
|
||||||
|
|
||||||
impl SendBackend for mpsc::Sender<StoreAddr> {
|
impl SendBackend for mpsc::Sender<StoreAddr> {
|
||||||
fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> {
|
fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> {
|
||||||
self.send(addr).map_err(|_| addr)
|
self.send(addr).map_err(|_| addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StdVerifSender {
|
pub struct MpscVerifSender {
|
||||||
base: StdSenderBase<mpsc::Sender<StoreAddr>>,
|
base: StdSenderBase<mpsc::Sender<StoreAddr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verification sender with a [mpsc::Sender] backend.
|
/// Verification sender with a [mpsc::Sender] backend.
|
||||||
/// It implements the [VerificationSender] trait to be used as PUS Verification TM sender.
|
/// It implements the [VerificationSender] trait to be used as PUS Verification TM sender.
|
||||||
impl StdVerifSender {
|
impl MpscVerifSender {
|
||||||
pub fn new(tm_store: Arc<RwLock<LocalPool>>, tx: mpsc::Sender<StoreAddr>) -> Self {
|
pub fn new(tm_store: SharedPool, tx: mpsc::Sender<StoreAddr>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
base: StdSenderBase::new(tm_store, tx),
|
base: StdSenderBase::new(tm_store, tx),
|
||||||
}
|
}
|
||||||
@ -765,15 +776,15 @@ mod stdmod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
//noinspection RsTraitImplementation
|
||||||
impl VerificationSender<StdVerifSenderError> for StdVerifSender {
|
impl VerificationSender<StdVerifSenderError> for MpscVerifSender {
|
||||||
delegate!(
|
delegate!(
|
||||||
to self.base {
|
to self.base {
|
||||||
fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError<StdVerifSenderError>>;
|
fn send_verification_tm(&mut self, tm: PusTm) -> Result<(), VerificationError<StdVerifSenderError>>;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
unsafe impl Sync for StdVerifSender {}
|
unsafe impl Sync for MpscVerifSender {}
|
||||||
unsafe impl Send for StdVerifSender {}
|
unsafe impl Send for MpscVerifSender {}
|
||||||
|
|
||||||
impl SendBackend for crossbeam_channel::Sender<StoreAddr> {
|
impl SendBackend for crossbeam_channel::Sender<StoreAddr> {
|
||||||
fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> {
|
fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> {
|
||||||
@ -788,10 +799,7 @@ mod stdmod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CrossbeamVerifSender {
|
impl CrossbeamVerifSender {
|
||||||
pub fn new(
|
pub fn new(tm_store: SharedPool, tx: crossbeam_channel::Sender<StoreAddr>) -> Self {
|
||||||
tm_store: Arc<RwLock<LocalPool>>,
|
|
||||||
tx: crossbeam_channel::Sender<StoreAddr>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
base: StdSenderBase::new(tm_store, tx),
|
base: StdSenderBase::new(tm_store, tx),
|
||||||
}
|
}
|
||||||
@ -815,7 +823,7 @@ mod stdmod {
|
|||||||
&mut self,
|
&mut self,
|
||||||
tm: PusTm,
|
tm: PusTm,
|
||||||
) -> Result<(), VerificationError<StdVerifSenderError>> {
|
) -> Result<(), VerificationError<StdVerifSenderError>> {
|
||||||
let operation = |mut mg: RwLockWriteGuard<LocalPool>| {
|
let operation = |mut mg: RwLockWriteGuard<ShareablePoolProvider>| {
|
||||||
let (addr, buf) = mg.free_element(tm.len_packed()).map_err(|e| {
|
let (addr, buf) = mg.free_element(tm.len_packed()).map_err(|e| {
|
||||||
VerificationError::SendError(StdVerifSenderError::StoreError(e))
|
VerificationError::SendError(StdVerifSenderError::StoreError(e))
|
||||||
})?;
|
})?;
|
||||||
|
@ -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::ops::DerefMut;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use fsrc_core::pool::{LocalPool, PoolCfg};
|
use fsrc_core::pool::{LocalPool, PoolCfg, PoolProvider, SharedPool};
|
||||||
use fsrc_core::pus::verification::{
|
use fsrc_core::pus::verification::{
|
||||||
CrossbeamVerifSender, FailParams, RequestId, VerificationReporterCfg,
|
CrossbeamVerifSender, FailParams, RequestId, VerificationReporterCfg,
|
||||||
VerificationReporterWithSender,
|
VerificationReporterWithSender,
|
||||||
@ -28,7 +28,8 @@ fn test_shared_reporter() {
|
|||||||
let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8);
|
let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8);
|
||||||
// Shared pool object to store the verification PUS telemetry
|
// Shared pool object to store the verification PUS telemetry
|
||||||
let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
|
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_0 = Arc::new(RwLock::new(LocalPool::new(pool_cfg)));
|
||||||
let shared_tc_pool_1 = shared_tc_pool_0.clone();
|
let shared_tc_pool_1 = shared_tc_pool_0.clone();
|
||||||
let (tx, rx) = crossbeam_channel::bounded(5);
|
let (tx, rx) = crossbeam_channel::bounded(5);
|
||||||
|
@ -6,6 +6,7 @@ authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
|
delegate = "0.8"
|
||||||
|
|
||||||
[dependencies.spacepackets]
|
[dependencies.spacepackets]
|
||||||
path = "../spacepackets"
|
path = "../spacepackets"
|
||||||
|
@ -2,13 +2,16 @@ mod ccsds;
|
|||||||
mod pus;
|
mod pus;
|
||||||
mod tmtc;
|
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::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_core::tmtc::CcsdsError;
|
||||||
use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT};
|
use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT};
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
struct TmFunnel {
|
struct TmFunnel {
|
||||||
@ -19,7 +22,7 @@ struct TmFunnel {
|
|||||||
struct UdpTmtcServer {
|
struct UdpTmtcServer {
|
||||||
udp_tc_server: UdpTcServer<CcsdsError<()>>,
|
udp_tc_server: UdpTcServer<CcsdsError<()>>,
|
||||||
tm_rx: mpsc::Receiver<StoreAddr>,
|
tm_rx: mpsc::Receiver<StoreAddr>,
|
||||||
tm_store: Arc<Mutex<TmStore>>,
|
tm_store: SharedPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for UdpTmtcServer {}
|
unsafe impl Send for UdpTmtcServer {}
|
||||||
@ -28,13 +31,27 @@ fn main() {
|
|||||||
println!("Running OBSW example");
|
println!("Running OBSW example");
|
||||||
let pool_cfg = PoolCfg::new(vec![(8, 32), (4, 64), (2, 128)]);
|
let pool_cfg = PoolCfg::new(vec![(8, 32), (4, 64), (2, 128)]);
|
||||||
let tm_pool = LocalPool::new(pool_cfg);
|
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 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 (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 || {
|
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 || {
|
let jh1 = thread::spawn(move || {
|
||||||
|
@ -1,23 +1,31 @@
|
|||||||
use crate::TmStore;
|
use crate::tmtc::TmStore;
|
||||||
use fsrc_core::pool::StoreAddr;
|
use fsrc_core::pool::StoreAddr;
|
||||||
|
use fsrc_core::pus::verification::SharedStdVerifReporterWithSender;
|
||||||
use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper;
|
use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper;
|
||||||
use fsrc_core::tmtc::PusServiceProvider;
|
use fsrc_core::tmtc::PusServiceProvider;
|
||||||
use spacepackets::tc::{PusTc, PusTcSecondaryHeaderT};
|
use spacepackets::tc::{PusTc, PusTcSecondaryHeaderT};
|
||||||
use spacepackets::SpHeader;
|
use spacepackets::SpHeader;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::mpsc;
|
||||||
|
|
||||||
pub struct PusReceiver {
|
pub struct PusReceiver {
|
||||||
pub tm_helper: PusTmWithCdsShortHelper,
|
pub tm_helper: PusTmWithCdsShortHelper,
|
||||||
pub tm_tx: mpsc::Sender<StoreAddr>,
|
pub tm_tx: mpsc::Sender<StoreAddr>,
|
||||||
pub tm_store: Arc<Mutex<TmStore>>,
|
pub tm_store: TmStore,
|
||||||
|
pub verif_reporter: SharedStdVerifReporterWithSender,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusReceiver {
|
impl PusReceiver {
|
||||||
pub fn new(apid: u16, tm_tx: mpsc::Sender<StoreAddr>, tm_store: Arc<Mutex<TmStore>>) -> Self {
|
pub fn new(
|
||||||
|
apid: u16,
|
||||||
|
tm_tx: mpsc::Sender<StoreAddr>,
|
||||||
|
tm_store: TmStore,
|
||||||
|
verif_reporter: SharedStdVerifReporterWithSender,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tm_helper: PusTmWithCdsShortHelper::new(apid),
|
tm_helper: PusTmWithCdsShortHelper::new(apid),
|
||||||
tm_tx,
|
tm_tx,
|
||||||
tm_store,
|
tm_store,
|
||||||
|
verif_reporter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,11 +52,7 @@ impl PusReceiver {
|
|||||||
println!("Received PUS ping command TC[17,1]");
|
println!("Received PUS ping command TC[17,1]");
|
||||||
println!("Sending ping reply PUS TM[17,2]");
|
println!("Sending ping reply PUS TM[17,2]");
|
||||||
let ping_reply = self.tm_helper.create_pus_tm_timestamp_now(17, 2, None);
|
let ping_reply = self.tm_helper.create_pus_tm_timestamp_now(17, 2, None);
|
||||||
let addr = self
|
let addr = self.tm_store.add_pus_tm(&ping_reply);
|
||||||
.tm_store
|
|
||||||
.lock()
|
|
||||||
.expect("Locking TM store failed")
|
|
||||||
.add_pus_tm(&ping_reply);
|
|
||||||
self.tm_tx
|
self.tm_tx
|
||||||
.send(addr)
|
.send(addr)
|
||||||
.expect("Sending TM to TM funnel failed");
|
.expect("Sending TM to TM funnel failed");
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
use fsrc_core::hal::host::udp_server::{ReceiveResult, UdpTcServer};
|
use fsrc_core::hal::host::udp_server::{ReceiveResult, UdpTcServer};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::mpsc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::ccsds::CcsdsReceiver;
|
use crate::ccsds::CcsdsReceiver;
|
||||||
use crate::pus::PusReceiver;
|
use crate::pus::PusReceiver;
|
||||||
use crate::UdpTmtcServer;
|
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 fsrc_core::tmtc::{CcsdsDistributor, CcsdsError, PusDistributor};
|
||||||
use spacepackets::tm::PusTm;
|
use spacepackets::tm::PusTm;
|
||||||
|
|
||||||
pub const PUS_APID: u16 = 0x02;
|
pub const PUS_APID: u16 = 0x02;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct TmStore {
|
pub struct TmStore {
|
||||||
pub pool: LocalPool,
|
pub pool: SharedPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TmStore {
|
impl TmStore {
|
||||||
pub 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
|
let mut pg = self.pool.write().expect("Error locking TM store");
|
||||||
.pool
|
let (addr, buf) = pg.free_element(pus_tm.len_packed()).expect("Store error");
|
||||||
.free_element(pus_tm.len_packed())
|
|
||||||
.expect("Store error");
|
|
||||||
pus_tm
|
pus_tm
|
||||||
.write_to(buf)
|
.write_to(buf)
|
||||||
.expect("Writing PUS TM to store failed");
|
.expect("Writing PUS TM to store failed");
|
||||||
@ -33,10 +33,16 @@ impl TmStore {
|
|||||||
pub fn core_tmtc_task(
|
pub fn core_tmtc_task(
|
||||||
tm_creator_tx: mpsc::Sender<StoreAddr>,
|
tm_creator_tx: mpsc::Sender<StoreAddr>,
|
||||||
tm_server_rx: mpsc::Receiver<StoreAddr>,
|
tm_server_rx: mpsc::Receiver<StoreAddr>,
|
||||||
tm_store: Arc<Mutex<TmStore>>,
|
tm_store_helper: TmStore,
|
||||||
addr: SocketAddr,
|
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 pus_distributor = PusDistributor::new(Box::new(pus_receiver));
|
||||||
let ccsds_receiver = CcsdsReceiver {
|
let ccsds_receiver = CcsdsReceiver {
|
||||||
pus_handler: pus_distributor,
|
pus_handler: pus_distributor,
|
||||||
@ -44,10 +50,11 @@ pub fn core_tmtc_task(
|
|||||||
let ccsds_distributor = CcsdsDistributor::new(Box::new(ccsds_receiver));
|
let ccsds_distributor = CcsdsDistributor::new(Box::new(ccsds_receiver));
|
||||||
let udp_tc_server = UdpTcServer::new(addr, 2048, Box::new(ccsds_distributor))
|
let udp_tc_server = UdpTcServer::new(addr, 2048, Box::new(ccsds_distributor))
|
||||||
.expect("Creating UDP TMTC server failed");
|
.expect("Creating UDP TMTC server failed");
|
||||||
|
|
||||||
let mut udp_tmtc_server = UdpTmtcServer {
|
let mut udp_tmtc_server = UdpTmtcServer {
|
||||||
udp_tc_server,
|
udp_tc_server,
|
||||||
tm_rx: tm_server_rx,
|
tm_rx: tm_server_rx,
|
||||||
tm_store,
|
tm_store: tm_store_helper.pool.clone(),
|
||||||
};
|
};
|
||||||
loop {
|
loop {
|
||||||
core_tmtc_loop(&mut udp_tmtc_server);
|
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() {
|
while let Ok(addr) = udp_tmtc_server.tm_rx.try_recv() {
|
||||||
let mut store_lock = udp_tmtc_server
|
let mut store_lock = udp_tmtc_server
|
||||||
.tm_store
|
.tm_store
|
||||||
.lock()
|
.write()
|
||||||
.expect("Locking TM store failed");
|
.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");
|
let buf = pg.read().expect("Error reading TM pool data");
|
||||||
println!("Sending TM");
|
println!("Sending TM");
|
||||||
udp_tmtc_server
|
udp_tmtc_server
|
||||||
|
@ -27,10 +27,7 @@ impl FieldDataProvider for FixedFieldDataWrapper {
|
|||||||
type FieldDataTraitObj = Box<dyn FieldDataProvider>;
|
type FieldDataTraitObj = Box<dyn FieldDataProvider>;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (s0, r0): (
|
let (s0, r0): (Sender<FieldDataTraitObj>, Receiver<FieldDataTraitObj>) = bounded(5);
|
||||||
Sender<FieldDataTraitObj>,
|
|
||||||
Receiver<FieldDataTraitObj>,
|
|
||||||
) = bounded(5);
|
|
||||||
let data_wrapper = FixedFieldDataWrapper::from_two_u32(2, 3);
|
let data_wrapper = FixedFieldDataWrapper::from_two_u32(2, 3);
|
||||||
s0.send(Box::new(data_wrapper)).unwrap();
|
s0.send(Box::new(data_wrapper)).unwrap();
|
||||||
let jh0 = thread::spawn(move || {
|
let jh0 = thread::spawn(move || {
|
||||||
|
Loading…
Reference in New Issue
Block a user