Continue CFDP handlers #90
@ -73,11 +73,10 @@ features = ["all"]
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.spacepackets]
|
[dependencies.spacepackets]
|
||||||
version = "0.7.0-beta.2"
|
version = "0.7.0-beta.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
# git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git"
|
# git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git"
|
||||||
# rev = "79d26e1a6"
|
# rev = "297cfad22637d3b07a1b27abe56d9a607b5b82a7"
|
||||||
# branch = ""
|
|
||||||
|
|
||||||
[dependencies.cobs]
|
[dependencies.cobs]
|
||||||
git = "https://github.com/robamu/cobs.rs.git"
|
git = "https://github.com/robamu/cobs.rs.git"
|
||||||
@ -91,6 +90,7 @@ zerocopy = "0.7"
|
|||||||
once_cell = "1.13"
|
once_cell = "1.13"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
tempfile = "3"
|
||||||
|
|
||||||
[dev-dependencies.postcard]
|
[dev-dependencies.postcard]
|
||||||
version = "1"
|
version = "1"
|
||||||
|
File diff suppressed because it is too large
Load Diff
370
satrs-core/src/cfdp/filestore.rs
Normal file
370
satrs-core/src/cfdp/filestore.rs
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
use alloc::string::{String, ToString};
|
||||||
|
use core::fmt::Display;
|
||||||
|
use crc::{Crc, CRC_32_CKSUM};
|
||||||
|
use spacepackets::cfdp::ChecksumType;
|
||||||
|
use spacepackets::ByteConversionError;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::error::Error;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use stdmod::*;
|
||||||
|
|
||||||
|
pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum FilestoreError {
|
||||||
|
FileDoesNotExist,
|
||||||
|
FileAlreadyExists,
|
||||||
|
DirDoesNotExist,
|
||||||
|
Permission,
|
||||||
|
IsNotFile,
|
||||||
|
IsNotDirectory,
|
||||||
|
ByteConversion(ByteConversionError),
|
||||||
|
Io {
|
||||||
|
raw_errno: Option<i32>,
|
||||||
|
string: String,
|
||||||
|
},
|
||||||
|
ChecksumTypeNotImplemented(ChecksumType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ByteConversionError> for FilestoreError {
|
||||||
|
fn from(value: ByteConversionError) -> Self {
|
||||||
|
Self::ByteConversion(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FilestoreError {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
FilestoreError::FileDoesNotExist => {
|
||||||
|
write!(f, "file does not exist")
|
||||||
|
}
|
||||||
|
FilestoreError::FileAlreadyExists => {
|
||||||
|
write!(f, "file already exists")
|
||||||
|
}
|
||||||
|
FilestoreError::DirDoesNotExist => {
|
||||||
|
write!(f, "directory does not exist")
|
||||||
|
}
|
||||||
|
FilestoreError::Permission => {
|
||||||
|
write!(f, "permission error")
|
||||||
|
}
|
||||||
|
FilestoreError::IsNotFile => {
|
||||||
|
write!(f, "is not a file")
|
||||||
|
}
|
||||||
|
FilestoreError::IsNotDirectory => {
|
||||||
|
write!(f, "is not a directory")
|
||||||
|
}
|
||||||
|
FilestoreError::ByteConversion(e) => {
|
||||||
|
write!(f, "filestore error: {e}")
|
||||||
|
}
|
||||||
|
FilestoreError::Io { raw_errno, string } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"filestore generic IO error with raw errno {:?}: {}",
|
||||||
|
raw_errno, string
|
||||||
|
)
|
||||||
|
}
|
||||||
|
FilestoreError::ChecksumTypeNotImplemented(checksum_type) => {
|
||||||
|
write!(f, "checksum {:?} not implemented", checksum_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for FilestoreError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
FilestoreError::ByteConversion(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl From<std::io::Error> for FilestoreError {
|
||||||
|
fn from(value: std::io::Error) -> Self {
|
||||||
|
Self::Io {
|
||||||
|
raw_errno: value.raw_os_error(),
|
||||||
|
string: value.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait VirtualFilestore {
|
||||||
|
fn create_file(&self, file_path: &str) -> Result<(), FilestoreError>;
|
||||||
|
|
||||||
|
fn remove_file(&self, file_path: &str) -> Result<(), FilestoreError>;
|
||||||
|
|
||||||
|
/// Truncating a file means deleting all its data so the resulting file is empty.
|
||||||
|
/// This can be more efficient than removing and re-creating a file.
|
||||||
|
fn truncate_file(&self, file_path: &str) -> Result<(), FilestoreError>;
|
||||||
|
|
||||||
|
fn remove_dir(&self, file_path: &str, all: bool) -> Result<(), FilestoreError>;
|
||||||
|
|
||||||
|
fn read_data(
|
||||||
|
&self,
|
||||||
|
file_path: &str,
|
||||||
|
offset: u64,
|
||||||
|
read_len: u64,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Result<(), FilestoreError>;
|
||||||
|
|
||||||
|
fn write_data(&self, file: &str, offset: u64, buf: &[u8]) -> Result<(), FilestoreError>;
|
||||||
|
|
||||||
|
fn filename_from_full_path<'a>(&self, path: &'a str) -> Option<&'a str>;
|
||||||
|
|
||||||
|
fn is_file(&self, path: &str) -> bool;
|
||||||
|
|
||||||
|
fn is_dir(&self, path: &str) -> bool {
|
||||||
|
!self.is_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists(&self, path: &str) -> bool;
|
||||||
|
|
||||||
|
/// This special function is the CFDP specific abstraction to verify the checksum of a file.
|
||||||
|
/// This allows to keep OS specific details like reading the whole file in the most efficient
|
||||||
|
/// manner inside the file system abstraction.
|
||||||
|
fn checksum_verify(
|
||||||
|
&self,
|
||||||
|
file_path: &str,
|
||||||
|
checksum_type: ChecksumType,
|
||||||
|
expected_checksum: u32,
|
||||||
|
verification_buf: &mut [u8],
|
||||||
|
) -> Result<bool, FilestoreError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub mod stdmod {
|
||||||
|
use super::*;
|
||||||
|
use std::{
|
||||||
|
fs::{self, File, OpenOptions},
|
||||||
|
io::{BufReader, Read, Seek, SeekFrom, Write},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NativeFilestore {}
|
||||||
|
|
||||||
|
impl VirtualFilestore for NativeFilestore {
|
||||||
|
fn create_file(&self, file_path: &str) -> Result<(), FilestoreError> {
|
||||||
|
if self.exists(file_path) {
|
||||||
|
return Err(FilestoreError::FileAlreadyExists);
|
||||||
|
}
|
||||||
|
File::create(file_path)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_file(&self, file_path: &str) -> Result<(), FilestoreError> {
|
||||||
|
if !self.exists(file_path) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if !self.is_file(file_path) {
|
||||||
|
return Err(FilestoreError::IsNotFile);
|
||||||
|
}
|
||||||
|
fs::remove_file(file_path)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn truncate_file(&self, file_path: &str) -> Result<(), FilestoreError> {
|
||||||
|
if !self.exists(file_path) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if !self.is_file(file_path) {
|
||||||
|
return Err(FilestoreError::IsNotFile);
|
||||||
|
}
|
||||||
|
OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(file_path)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir(&self, dir_path: &str, all: bool) -> Result<(), FilestoreError> {
|
||||||
|
if !self.exists(dir_path) {
|
||||||
|
return Err(FilestoreError::DirDoesNotExist);
|
||||||
|
}
|
||||||
|
if !self.is_dir(dir_path) {
|
||||||
|
return Err(FilestoreError::IsNotDirectory);
|
||||||
|
}
|
||||||
|
if !all {
|
||||||
|
fs::remove_dir(dir_path)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
fs::remove_dir_all(dir_path)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_data(
|
||||||
|
&self,
|
||||||
|
file_name: &str,
|
||||||
|
offset: u64,
|
||||||
|
read_len: u64,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Result<(), FilestoreError> {
|
||||||
|
if buf.len() < read_len as usize {
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: read_len as usize,
|
||||||
|
}
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
if !self.exists(file_name) {
|
||||||
|
return Err(FilestoreError::FileDoesNotExist);
|
||||||
|
}
|
||||||
|
if !self.is_file(file_name) {
|
||||||
|
return Err(FilestoreError::IsNotFile);
|
||||||
|
}
|
||||||
|
let mut file = File::open(file_name)?;
|
||||||
|
file.seek(SeekFrom::Start(offset))?;
|
||||||
|
file.read_exact(&mut buf[0..read_len as usize])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_data(&self, file: &str, offset: u64, buf: &[u8]) -> Result<(), FilestoreError> {
|
||||||
|
if !self.exists(file) {
|
||||||
|
return Err(FilestoreError::FileDoesNotExist);
|
||||||
|
}
|
||||||
|
if !self.is_file(file) {
|
||||||
|
return Err(FilestoreError::IsNotFile);
|
||||||
|
}
|
||||||
|
let mut file = OpenOptions::new().write(true).open(file)?;
|
||||||
|
file.seek(SeekFrom::Start(offset))?;
|
||||||
|
file.write_all(buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filename_from_full_path<'a>(&self, path: &'a str) -> Option<&'a str> {
|
||||||
|
// Convert the path string to a Path
|
||||||
|
let path = Path::new(path);
|
||||||
|
|
||||||
|
// Extract the file name using the file_name() method
|
||||||
|
path.file_name().and_then(|name| name.to_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_file(&self, path: &str) -> bool {
|
||||||
|
let path = Path::new(path);
|
||||||
|
path.is_file()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_dir(&self, path: &str) -> bool {
|
||||||
|
let path = Path::new(path);
|
||||||
|
path.is_dir()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists(&self, path: &str) -> bool {
|
||||||
|
let path = Path::new(path);
|
||||||
|
if !path.exists() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checksum_verify(
|
||||||
|
&self,
|
||||||
|
file_path: &str,
|
||||||
|
checksum_type: ChecksumType,
|
||||||
|
expected_checksum: u32,
|
||||||
|
verification_buf: &mut [u8],
|
||||||
|
) -> Result<bool, FilestoreError> {
|
||||||
|
match checksum_type {
|
||||||
|
ChecksumType::Modular => {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
ChecksumType::Crc32 => {
|
||||||
|
let mut digest = CRC_32.digest();
|
||||||
|
let file_to_check = File::open(file_path)?;
|
||||||
|
let mut buf_reader = BufReader::new(file_to_check);
|
||||||
|
loop {
|
||||||
|
let bytes_read = buf_reader.read(verification_buf)?;
|
||||||
|
if bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
digest.update(&verification_buf[0..bytes_read]);
|
||||||
|
}
|
||||||
|
if digest.finalize() == expected_checksum {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
ChecksumType::NullChecksum => Ok(true),
|
||||||
|
_ => Err(FilestoreError::ChecksumTypeNotImplemented(checksum_type)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{fs, path::Path, println};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
const NATIVE_FS: NativeFilestore = NativeFilestore {};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic_native_filestore_create() {
|
||||||
|
let tmpdir = tempdir().expect("creating tmpdir failed");
|
||||||
|
let file_path = tmpdir.path().join("test.txt");
|
||||||
|
let result =
|
||||||
|
NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let path = Path::new(&file_path);
|
||||||
|
assert!(path.exists());
|
||||||
|
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
|
||||||
|
assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
|
||||||
|
fs::remove_dir_all(tmpdir).expect("clearing tmpdir failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic_native_fs_exists() {
|
||||||
|
let tmpdir = tempdir().expect("creating tmpdir failed");
|
||||||
|
let file_path = tmpdir.path().join("test.txt");
|
||||||
|
assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
|
||||||
|
NATIVE_FS
|
||||||
|
.create_file(file_path.to_str().expect("getting str for file failed"))
|
||||||
|
.unwrap();
|
||||||
|
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
|
||||||
|
assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
|
||||||
|
fs::remove_dir_all(tmpdir).expect("clearing tmpdir failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic_native_fs_write() {
|
||||||
|
let tmpdir = tempdir().expect("creating tmpdir failed");
|
||||||
|
let file_path = tmpdir.path().join("test.txt");
|
||||||
|
assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
|
||||||
|
NATIVE_FS
|
||||||
|
.create_file(file_path.to_str().expect("getting str for file failed"))
|
||||||
|
.unwrap();
|
||||||
|
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
|
||||||
|
assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
|
||||||
|
println!("{}", file_path.to_str().unwrap());
|
||||||
|
let write_data = "hello world\n";
|
||||||
|
NATIVE_FS
|
||||||
|
.write_data(file_path.to_str().unwrap(), 0, write_data.as_bytes())
|
||||||
|
.expect("writing to file failed");
|
||||||
|
let read_back = fs::read_to_string(file_path).expect("reading back data failed");
|
||||||
|
assert_eq!(read_back, write_data);
|
||||||
|
fs::remove_dir_all(tmpdir).expect("clearing tmpdir failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic_native_fs_read() {
|
||||||
|
let tmpdir = tempdir().expect("creating tmpdir failed");
|
||||||
|
let file_path = tmpdir.path().join("test.txt");
|
||||||
|
assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()));
|
||||||
|
NATIVE_FS
|
||||||
|
.create_file(file_path.to_str().expect("getting str for file failed"))
|
||||||
|
.unwrap();
|
||||||
|
assert!(NATIVE_FS.exists(file_path.to_str().unwrap()));
|
||||||
|
assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()));
|
||||||
|
println!("{}", file_path.to_str().unwrap());
|
||||||
|
let write_data = "hello world\n";
|
||||||
|
NATIVE_FS
|
||||||
|
.write_data(file_path.to_str().unwrap(), 0, write_data.as_bytes())
|
||||||
|
.expect("writing to file failed");
|
||||||
|
let read_back = fs::read_to_string(file_path).expect("reading back data failed");
|
||||||
|
assert_eq!(read_back, write_data);
|
||||||
|
fs::remove_dir_all(tmpdir).expect("clearing tmpdir failed");
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,13 @@
|
|||||||
|
//! This module contains the implementation of the CFDP high level classes as specified in the
|
||||||
|
//! CCSDS 727.0-B-5.
|
||||||
|
use core::{cell::RefCell, fmt::Debug, hash::Hash};
|
||||||
|
|
||||||
use crc::{Crc, CRC_32_CKSUM};
|
use crc::{Crc, CRC_32_CKSUM};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
cfdp::{
|
cfdp::{
|
||||||
pdu::{FileDirectiveType, PduError, PduHeader},
|
pdu::{FileDirectiveType, PduError, PduHeader},
|
||||||
ChecksumType, PduType, TransmissionMode,
|
ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
|
||||||
},
|
},
|
||||||
util::UnsignedByteField,
|
util::UnsignedByteField,
|
||||||
};
|
};
|
||||||
@ -14,6 +19,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod dest;
|
pub mod dest;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub mod filestore;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod source;
|
pub mod source;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
@ -24,7 +31,27 @@ pub enum EntityType {
|
|||||||
Receiving,
|
Receiving,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic abstraction for a check timer which has different functionality depending on whether
|
pub enum TimerContext {
|
||||||
|
CheckLimit {
|
||||||
|
local_id: UnsignedByteField,
|
||||||
|
remote_id: UnsignedByteField,
|
||||||
|
entity_type: EntityType,
|
||||||
|
},
|
||||||
|
NakActivity {
|
||||||
|
expiry_time_seconds: f32,
|
||||||
|
},
|
||||||
|
PositiveAck {
|
||||||
|
expiry_time_seconds: f32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic abstraction for a check timer which is used by 3 mechanisms of the CFDP protocol.
|
||||||
|
///
|
||||||
|
/// ## 1. Check limit handling
|
||||||
|
///
|
||||||
|
/// The first mechanism is the check limit handling for unacknowledged transfers as specified
|
||||||
|
/// in 4.6.3.2 and 4.6.3.3 of the CFDP standard.
|
||||||
|
/// For this mechanism, the timer has different functionality depending on whether
|
||||||
/// the using entity is the sending entity or the receiving entity for the unacknowledged
|
/// the using entity is the sending entity or the receiving entity for the unacknowledged
|
||||||
/// transmission mode.
|
/// transmission mode.
|
||||||
///
|
///
|
||||||
@ -35,30 +62,40 @@ pub enum EntityType {
|
|||||||
/// For the receiving entity, this timer determines the expiry period for incrementing a check
|
/// For the receiving entity, this timer determines the expiry period for incrementing a check
|
||||||
/// counter after an EOF PDU is received for an incomplete file transfer. This allows out-of-order
|
/// counter after an EOF PDU is received for an incomplete file transfer. This allows out-of-order
|
||||||
/// reception of file data PDUs and EOF PDUs. Also see 4.6.3.3 of the CFDP standard.
|
/// reception of file data PDUs and EOF PDUs. Also see 4.6.3.3 of the CFDP standard.
|
||||||
pub trait CheckTimerProvider {
|
///
|
||||||
|
/// ## 2. NAK activity limit
|
||||||
|
///
|
||||||
|
/// The timer will be used to perform the NAK activity check as specified in 4.6.4.7 of the CFDP
|
||||||
|
/// standard. The expiration period will be provided by the NAK timer expiration limit of the
|
||||||
|
/// remote entity configuration.
|
||||||
|
///
|
||||||
|
/// ## 3. Positive ACK procedures
|
||||||
|
///
|
||||||
|
/// The timer will be used to perform the Positive Acknowledgement Procedures as specified in
|
||||||
|
/// 4.7. 1of the CFDP standard. The expiration period will be provided by the Positive ACK timer
|
||||||
|
/// interval of the remote entity configuration.
|
||||||
|
pub trait CheckTimer: Debug {
|
||||||
fn has_expired(&self) -> bool;
|
fn has_expired(&self) -> bool;
|
||||||
|
fn reset(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic trait which allows CFDP entities to create check timers which are required to
|
/// A generic trait which allows CFDP entities to create check timers which are required to
|
||||||
/// implement special procedures in unacknowledged transmission mode, as specified in 4.6.3.2
|
/// implement special procedures in unacknowledged transmission mode, as specified in 4.6.3.2
|
||||||
/// and 4.6.3.3. The [CheckTimerProvider] provides more information about the purpose of the
|
/// and 4.6.3.3. The [CheckTimer] documentation provides more information about the purpose of the
|
||||||
/// check timer.
|
/// check timer in the context of CFDP.
|
||||||
///
|
///
|
||||||
/// This trait also allows the creation of different check timers depending on
|
/// This trait also allows the creation of different check timers depending on context and purpose
|
||||||
/// the ID of the local entity, the ID of the remote entity for a given transaction, and the
|
/// of the timer, the runtime environment (e.g. standard clock timer vs. timer using a RTC) or
|
||||||
/// type of entity.
|
/// other factors.
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub trait CheckTimerCreator {
|
pub trait CheckTimerCreator {
|
||||||
fn get_check_timer_provider(
|
fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CheckTimer>;
|
||||||
local_id: &UnsignedByteField,
|
|
||||||
remote_id: &UnsignedByteField,
|
|
||||||
entity_type: EntityType,
|
|
||||||
) -> Box<dyn CheckTimerProvider>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simple implementation of the [CheckTimerProvider] trait assuming a standard runtime.
|
/// Simple implementation of the [CheckTimerCreator] trait assuming a standard runtime.
|
||||||
/// It also assumes that a second accuracy of the check timer period is sufficient.
|
/// It also assumes that a second accuracy of the check timer period is sufficient.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct StdCheckTimer {
|
pub struct StdCheckTimer {
|
||||||
expiry_time_seconds: u64,
|
expiry_time_seconds: u64,
|
||||||
start_time: std::time::Instant,
|
start_time: std::time::Instant,
|
||||||
@ -75,7 +112,7 @@ impl StdCheckTimer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl CheckTimerProvider for StdCheckTimer {
|
impl CheckTimer for StdCheckTimer {
|
||||||
fn has_expired(&self) -> bool {
|
fn has_expired(&self) -> bool {
|
||||||
let elapsed_time = self.start_time.elapsed();
|
let elapsed_time = self.start_time.elapsed();
|
||||||
if elapsed_time.as_secs() > self.expiry_time_seconds {
|
if elapsed_time.as_secs() > self.expiry_time_seconds {
|
||||||
@ -83,24 +120,322 @@ impl CheckTimerProvider for StdCheckTimer {
|
|||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.start_time = std::time::Instant::now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// This structure models the remote entity configuration information as specified in chapter 8.3
|
||||||
|
/// of the CFDP standard.
|
||||||
|
|
||||||
|
/// Some of the fields which were not considered necessary for the Rust implementation
|
||||||
|
/// were omitted. Some other fields which are not contained inside the standard but are considered
|
||||||
|
/// necessary for the Rust implementation are included.
|
||||||
|
///
|
||||||
|
/// ## Notes on Positive Acknowledgment Procedures
|
||||||
|
///
|
||||||
|
/// The `positive_ack_timer_interval_seconds` and `positive_ack_timer_expiration_limit` will
|
||||||
|
/// be used for positive acknowledgement procedures as specified in CFDP chapter 4.7. The sending
|
||||||
|
/// entity will start the timer for any PDUs where an acknowledgment is required (e.g. EOF PDU).
|
||||||
|
/// Once the expected ACK response has not been received for that interval, as counter will be
|
||||||
|
/// incremented and the timer will be reset. Once the counter exceeds the
|
||||||
|
/// `positive_ack_timer_expiration_limit`, a Positive ACK Limit Reached fault will be declared.
|
||||||
|
///
|
||||||
|
/// ## Notes on Deferred Lost Segment Procedures
|
||||||
|
///
|
||||||
|
/// This procedure will be active if an EOF (No Error) PDU is received in acknowledged mode. After
|
||||||
|
/// issuing the NAK sequence which has the whole file scope, a timer will be started. The timer is
|
||||||
|
/// reset when missing segments or missing metadata is received. The timer will be deactivated if
|
||||||
|
/// all missing data is received. If the timer expires, a new NAK sequence will be issued and a
|
||||||
|
/// counter will be incremented, which can lead to a NAK Limit Reached fault being declared.
|
||||||
|
///
|
||||||
|
/// ## Fields
|
||||||
|
///
|
||||||
|
/// * `entity_id` - The ID of the remote entity.
|
||||||
|
/// * `max_packet_len` - This determines of all PDUs generated for that remote entity in addition
|
||||||
|
/// to the `max_file_segment_len` attribute which also determines the size of file data PDUs.
|
||||||
|
/// * `max_file_segment_len` The maximum file segment length which determines the maximum size
|
||||||
|
/// of file data PDUs in addition to the `max_packet_len` attribute. If this field is set
|
||||||
|
/// to None, the maximum file segment length will be derived from the maximum packet length.
|
||||||
|
/// If this has some value which is smaller than the segment value derived from
|
||||||
|
/// `max_packet_len`, this value will be picked.
|
||||||
|
/// * `closure_requested_by_default` - If the closure requested field is not supplied as part of
|
||||||
|
/// the Put Request, it will be determined from this field in the remote configuration.
|
||||||
|
/// * `crc_on_transmission_by_default` - If the CRC option is not supplied as part of the Put
|
||||||
|
/// Request, it will be determined from this field in the remote configuration.
|
||||||
|
/// * `default_transmission_mode` - If the transmission mode is not supplied as part of the
|
||||||
|
/// Put Request, it will be determined from this field in the remote configuration.
|
||||||
|
/// * `disposition_on_cancellation` - Determines whether an incomplete received file is discard on
|
||||||
|
/// transaction cancellation. Defaults to False.
|
||||||
|
/// * `default_crc_type` - Default checksum type used to calculate for all file transmissions to
|
||||||
|
/// this remote entity.
|
||||||
|
/// * `check_limit` - This timer determines the expiry period for incrementing a check counter
|
||||||
|
/// after an EOF PDU is received for an incomplete file transfer. This allows out-of-order
|
||||||
|
/// reception of file data PDUs and EOF PDUs. Also see 4.6.3.3 of the CFDP standard. Defaults to
|
||||||
|
/// 2, so the check limit timer may expire twice.
|
||||||
|
/// * `positive_ack_timer_interval_seconds`- See the notes on the Positive Acknowledgment
|
||||||
|
/// Procedures inside the class documentation. Expected as floating point seconds. Defaults to
|
||||||
|
/// 10 seconds.
|
||||||
|
/// * `positive_ack_timer_expiration_limit` - See the notes on the Positive Acknowledgment
|
||||||
|
/// Procedures inside the class documentation. Defaults to 2, so the timer may expire twice.
|
||||||
|
/// * `immediate_nak_mode` - Specifies whether a NAK sequence should be issued immediately when a
|
||||||
|
/// file data gap or lost metadata is detected in the acknowledged mode. Defaults to True.
|
||||||
|
/// * `nak_timer_interval_seconds` - See the notes on the Deferred Lost Segment Procedure inside
|
||||||
|
/// the class documentation. Expected as floating point seconds. Defaults to 10 seconds.
|
||||||
|
/// * `nak_timer_expiration_limit` - See the notes on the Deferred Lost Segment Procedure inside
|
||||||
|
/// the class documentation. Defaults to 2, so the timer may expire two times.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct RemoteEntityConfig {
|
pub struct RemoteEntityConfig {
|
||||||
pub entity_id: UnsignedByteField,
|
pub entity_id: UnsignedByteField,
|
||||||
|
pub max_packet_len: usize,
|
||||||
pub max_file_segment_len: usize,
|
pub max_file_segment_len: usize,
|
||||||
pub closure_requeted_by_default: bool,
|
pub closure_requested_by_default: bool,
|
||||||
pub crc_on_transmission_by_default: bool,
|
pub crc_on_transmission_by_default: bool,
|
||||||
pub default_transmission_mode: TransmissionMode,
|
pub default_transmission_mode: TransmissionMode,
|
||||||
pub default_crc_type: ChecksumType,
|
pub default_crc_type: ChecksumType,
|
||||||
|
pub positive_ack_timer_interval_seconds: f32,
|
||||||
|
pub positive_ack_timer_expiration_limit: u32,
|
||||||
pub check_limit: u32,
|
pub check_limit: u32,
|
||||||
|
pub disposition_on_cancellation: bool,
|
||||||
|
pub immediate_nak_mode: bool,
|
||||||
|
pub nak_timer_interval_seconds: f32,
|
||||||
|
pub nak_timer_expiration_limit: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RemoteEntityConfig {
|
||||||
|
pub fn new_with_default_values(
|
||||||
|
entity_id: UnsignedByteField,
|
||||||
|
max_file_segment_len: usize,
|
||||||
|
max_packet_len: usize,
|
||||||
|
closure_requested_by_default: bool,
|
||||||
|
crc_on_transmission_by_default: bool,
|
||||||
|
default_transmission_mode: TransmissionMode,
|
||||||
|
default_crc_type: ChecksumType,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
entity_id,
|
||||||
|
max_file_segment_len,
|
||||||
|
max_packet_len,
|
||||||
|
closure_requested_by_default,
|
||||||
|
crc_on_transmission_by_default,
|
||||||
|
default_transmission_mode,
|
||||||
|
default_crc_type,
|
||||||
|
check_limit: 2,
|
||||||
|
positive_ack_timer_interval_seconds: 10.0,
|
||||||
|
positive_ack_timer_expiration_limit: 2,
|
||||||
|
disposition_on_cancellation: false,
|
||||||
|
immediate_nak_mode: true,
|
||||||
|
nak_timer_interval_seconds: 10.0,
|
||||||
|
nak_timer_expiration_limit: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RemoteEntityConfigProvider {
|
pub trait RemoteEntityConfigProvider {
|
||||||
fn get_remote_config(&self, remote_id: &UnsignedByteField) -> Option<&RemoteEntityConfig>;
|
/// Retrieve the remote entity configuration for the given remote ID.
|
||||||
|
fn get_remote_config(&self, remote_id: u64) -> Option<&RemoteEntityConfig>;
|
||||||
|
fn get_remote_config_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig>;
|
||||||
|
/// Add a new remote configuration. Return [true] if the configuration was
|
||||||
|
/// inserted successfully, and [false] if a configuration already exists.
|
||||||
|
fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool;
|
||||||
|
/// Remote a configuration. Returns [true] if the configuration was removed successfully,
|
||||||
|
/// and [false] if no configuration exists for the given remote ID.
|
||||||
|
fn remove_config(&mut self, remote_id: u64) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[cfg(feature = "std")]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct StdRemoteEntityConfigProvider {
|
||||||
|
remote_cfg_table: HashMap<u64, RemoteEntityConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl RemoteEntityConfigProvider for StdRemoteEntityConfigProvider {
|
||||||
|
fn get_remote_config(&self, remote_id: u64) -> Option<&RemoteEntityConfig> {
|
||||||
|
self.remote_cfg_table.get(&remote_id)
|
||||||
|
}
|
||||||
|
fn get_remote_config_mut(&mut self, remote_id: u64) -> Option<&mut RemoteEntityConfig> {
|
||||||
|
self.remote_cfg_table.get_mut(&remote_id)
|
||||||
|
}
|
||||||
|
fn add_config(&mut self, cfg: &RemoteEntityConfig) -> bool {
|
||||||
|
self.remote_cfg_table
|
||||||
|
.insert(cfg.entity_id.value(), *cfg)
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
fn remove_config(&mut self, remote_id: u64) -> bool {
|
||||||
|
self.remote_cfg_table.remove(&remote_id).is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This trait introduces some callbacks which will be called when a particular CFDP fault
|
||||||
|
/// handler is called.
|
||||||
|
///
|
||||||
|
/// It is passed into the CFDP handlers as part of the [DefaultFaultHandler] and the local entity
|
||||||
|
/// configuration and provides a way to specify custom user error handlers. This allows to
|
||||||
|
/// implement some CFDP features like fault handler logging, which would not be possible
|
||||||
|
/// generically otherwise.
|
||||||
|
///
|
||||||
|
/// For each error reported by the [DefaultFaultHandler], the appropriate fault handler callback
|
||||||
|
/// will be called depending on the [FaultHandlerCode].
|
||||||
|
pub trait UserFaultHandler {
|
||||||
|
fn notice_of_suspension_cb(
|
||||||
|
&mut self,
|
||||||
|
transaction_id: TransactionId,
|
||||||
|
cond: ConditionCode,
|
||||||
|
progress: u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn notice_of_cancellation_cb(
|
||||||
|
&mut self,
|
||||||
|
transaction_id: TransactionId,
|
||||||
|
cond: ConditionCode,
|
||||||
|
progress: u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn abandoned_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
|
||||||
|
|
||||||
|
fn ignore_cb(&mut self, transaction_id: TransactionId, cond: ConditionCode, progress: u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This structure is used to implement the fault handling as specified in chapter 4.8 of the CFDP
|
||||||
|
/// standard.
|
||||||
|
///
|
||||||
|
/// It does so by mapping each applicable [spacepackets::cfdp::ConditionCode] to a fault handler
|
||||||
|
/// which is denoted by the four [spacepackets::cfdp::FaultHandlerCode]s. This code is used
|
||||||
|
/// to select the error handling inside the CFDP handler itself in addition to dispatching to a
|
||||||
|
/// user-provided callback function provided by the [UserFaultHandler].
|
||||||
|
///
|
||||||
|
/// Some note on the provided default settings:
|
||||||
|
///
|
||||||
|
/// - Checksum failures will be ignored by default. This is because for unacknowledged transfers,
|
||||||
|
/// cancelling the transfer immediately would interfere with the check limit mechanism specified
|
||||||
|
/// in chapter 4.6.3.3.
|
||||||
|
/// - Unsupported checksum types will also be ignored by default. Even if the checksum type is
|
||||||
|
/// not supported the file transfer might still have worked properly.
|
||||||
|
///
|
||||||
|
/// For all other faults, the default fault handling operation will be to cancel the transaction.
|
||||||
|
/// These defaults can be overriden by using the [Self::set_fault_handler] method.
|
||||||
|
/// Please note that in any case, fault handler overrides can be specified by the sending CFDP
|
||||||
|
/// entity.
|
||||||
|
pub struct DefaultFaultHandler {
|
||||||
|
handler_array: [FaultHandlerCode; 10],
|
||||||
|
// Could also change the user fault handler trait to have non mutable methods, but that limits
|
||||||
|
// flexbility on the user side..
|
||||||
|
user_fault_handler: RefCell<Box<dyn UserFaultHandler + Send>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefaultFaultHandler {
|
||||||
|
fn condition_code_to_array_index(conditon_code: ConditionCode) -> Option<usize> {
|
||||||
|
Some(match conditon_code {
|
||||||
|
ConditionCode::PositiveAckLimitReached => 0,
|
||||||
|
ConditionCode::KeepAliveLimitReached => 1,
|
||||||
|
ConditionCode::InvalidTransmissionMode => 2,
|
||||||
|
ConditionCode::FilestoreRejection => 3,
|
||||||
|
ConditionCode::FileChecksumFailure => 4,
|
||||||
|
ConditionCode::FileSizeError => 5,
|
||||||
|
ConditionCode::NakLimitReached => 6,
|
||||||
|
ConditionCode::InactivityDetected => 7,
|
||||||
|
ConditionCode::CheckLimitReached => 8,
|
||||||
|
ConditionCode::UnsupportedChecksumType => 9,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fault_handler(
|
||||||
|
&mut self,
|
||||||
|
condition_code: ConditionCode,
|
||||||
|
fault_handler: FaultHandlerCode,
|
||||||
|
) {
|
||||||
|
let array_idx = Self::condition_code_to_array_index(condition_code);
|
||||||
|
if array_idx.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.handler_array[array_idx.unwrap()] = fault_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(user_fault_handler: Box<dyn UserFaultHandler + Send>) -> Self {
|
||||||
|
let mut init_array = [FaultHandlerCode::NoticeOfCancellation; 10];
|
||||||
|
init_array
|
||||||
|
[Self::condition_code_to_array_index(ConditionCode::FileChecksumFailure).unwrap()] =
|
||||||
|
FaultHandlerCode::IgnoreError;
|
||||||
|
init_array[Self::condition_code_to_array_index(ConditionCode::UnsupportedChecksumType)
|
||||||
|
.unwrap()] = FaultHandlerCode::IgnoreError;
|
||||||
|
Self {
|
||||||
|
handler_array: init_array,
|
||||||
|
user_fault_handler: RefCell::new(user_fault_handler),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fault_handler(&self, condition_code: ConditionCode) -> FaultHandlerCode {
|
||||||
|
let array_idx = Self::condition_code_to_array_index(condition_code);
|
||||||
|
if array_idx.is_none() {
|
||||||
|
return FaultHandlerCode::IgnoreError;
|
||||||
|
}
|
||||||
|
self.handler_array[array_idx.unwrap()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn report_fault(
|
||||||
|
&self,
|
||||||
|
transaction_id: TransactionId,
|
||||||
|
condition: ConditionCode,
|
||||||
|
progress: u64,
|
||||||
|
) -> FaultHandlerCode {
|
||||||
|
let array_idx = Self::condition_code_to_array_index(condition);
|
||||||
|
if array_idx.is_none() {
|
||||||
|
return FaultHandlerCode::IgnoreError;
|
||||||
|
}
|
||||||
|
let fh_code = self.handler_array[array_idx.unwrap()];
|
||||||
|
let mut handler_mut = self.user_fault_handler.borrow_mut();
|
||||||
|
match fh_code {
|
||||||
|
FaultHandlerCode::NoticeOfCancellation => {
|
||||||
|
handler_mut.notice_of_cancellation_cb(transaction_id, condition, progress);
|
||||||
|
}
|
||||||
|
FaultHandlerCode::NoticeOfSuspension => {
|
||||||
|
handler_mut.notice_of_suspension_cb(transaction_id, condition, progress);
|
||||||
|
}
|
||||||
|
FaultHandlerCode::IgnoreError => {
|
||||||
|
handler_mut.ignore_cb(transaction_id, condition, progress);
|
||||||
|
}
|
||||||
|
FaultHandlerCode::AbandonTransaction => {
|
||||||
|
handler_mut.abandoned_cb(transaction_id, condition, progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fh_code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IndicationConfig {
|
||||||
|
pub eof_sent: bool,
|
||||||
|
pub eof_recv: bool,
|
||||||
|
pub file_segment_recv: bool,
|
||||||
|
pub transaction_finished: bool,
|
||||||
|
pub suspended: bool,
|
||||||
|
pub resumed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IndicationConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
eof_sent: true,
|
||||||
|
eof_recv: true,
|
||||||
|
file_segment_recv: true,
|
||||||
|
transaction_finished: true,
|
||||||
|
suspended: true,
|
||||||
|
resumed: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LocalEntityConfig {
|
||||||
|
pub id: UnsignedByteField,
|
||||||
|
pub indication_cfg: IndicationConfig,
|
||||||
|
pub default_fault_handler: DefaultFaultHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The CFDP transaction ID of a CFDP transaction consists of the source entity ID and the sequence
|
||||||
|
/// number of that transfer which is also determined by the CFDP source entity.
|
||||||
|
#[derive(Debug, Eq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct TransactionId {
|
pub struct TransactionId {
|
||||||
source_id: UnsignedByteField,
|
source_id: UnsignedByteField,
|
||||||
@ -121,23 +456,38 @@ impl TransactionId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Hash for TransactionId {
|
||||||
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.source_id.value().hash(state);
|
||||||
|
self.seq_num.value().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for TransactionId {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.source_id.value() == other.source_id.value()
|
||||||
|
&& self.seq_num.value() == other.seq_num.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum TransactionStep {
|
pub enum TransactionStep {
|
||||||
Idle = 0,
|
Idle = 0,
|
||||||
TransactionStart = 1,
|
TransactionStart = 1,
|
||||||
ReceivingFileDataPdus = 2,
|
ReceivingFileDataPdus = 2,
|
||||||
SendingAckPdu = 3,
|
ReceivingFileDataPdusWithCheckLimitHandling = 3,
|
||||||
TransferCompletion = 4,
|
SendingAckPdu = 4,
|
||||||
SendingFinishedPdu = 5,
|
TransferCompletion = 5,
|
||||||
|
SendingFinishedPdu = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
Idle = 0,
|
Idle = 0,
|
||||||
BusyClass1Nacked = 2,
|
Busy = 1,
|
||||||
BusyClass2Acked = 3,
|
Suspended = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
|
pub const CRC_32: Crc<u32> = Crc::<u32>::new(&CRC_32_CKSUM);
|
||||||
@ -248,8 +598,8 @@ mod tests {
|
|||||||
pdu::{
|
pdu::{
|
||||||
eof::EofPdu,
|
eof::EofPdu,
|
||||||
file_data::FileDataPdu,
|
file_data::FileDataPdu,
|
||||||
metadata::{MetadataGenericParams, MetadataPdu},
|
metadata::{MetadataGenericParams, MetadataPduCreator},
|
||||||
CommonPduConfig, FileDirectiveType, PduHeader,
|
CommonPduConfig, FileDirectiveType, PduHeader, WritablePduPacket,
|
||||||
},
|
},
|
||||||
PduType,
|
PduType,
|
||||||
};
|
};
|
||||||
@ -272,7 +622,8 @@ mod tests {
|
|||||||
let dest_file_name = "hello-dest.txt";
|
let dest_file_name = "hello-dest.txt";
|
||||||
let src_lv = Lv::new_from_str(src_file_name).unwrap();
|
let src_lv = Lv::new_from_str(src_file_name).unwrap();
|
||||||
let dest_lv = Lv::new_from_str(dest_file_name).unwrap();
|
let dest_lv = Lv::new_from_str(dest_file_name).unwrap();
|
||||||
let metadata_pdu = MetadataPdu::new(pdu_header, metadata_params, src_lv, dest_lv, None);
|
let metadata_pdu =
|
||||||
|
MetadataPduCreator::new_no_opts(pdu_header, metadata_params, src_lv, dest_lv);
|
||||||
metadata_pdu
|
metadata_pdu
|
||||||
.write_to_bytes(&mut buf)
|
.write_to_bytes(&mut buf)
|
||||||
.expect("writing metadata PDU failed");
|
.expect("writing metadata PDU failed");
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
cfdp::{
|
cfdp::{
|
||||||
pdu::{
|
pdu::{
|
||||||
file_data::RecordContinuationState,
|
file_data::SegmentMetadata,
|
||||||
finished::{DeliveryCode, FileStatus},
|
finished::{DeliveryCode, FileStatus},
|
||||||
},
|
},
|
||||||
tlv::msg_to_user::MsgToUserTlv,
|
tlv::{msg_to_user::MsgToUserTlv, WritableTlv},
|
||||||
ConditionCode,
|
ConditionCode,
|
||||||
},
|
},
|
||||||
util::UnsignedByteField,
|
util::UnsignedByteField,
|
||||||
@ -30,13 +30,44 @@ pub struct MetadataReceivedParams<'src_file, 'dest_file, 'msgs_to_user> {
|
|||||||
pub msgs_to_user: &'msgs_to_user [MsgToUserTlv<'msgs_to_user>],
|
pub msgs_to_user: &'msgs_to_user [MsgToUserTlv<'msgs_to_user>],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OwnedMetadataRecvdParams {
|
||||||
|
pub id: TransactionId,
|
||||||
|
pub source_id: UnsignedByteField,
|
||||||
|
pub file_size: u64,
|
||||||
|
pub src_file_name: alloc::string::String,
|
||||||
|
pub dest_file_name: alloc::string::String,
|
||||||
|
pub msgs_to_user: alloc::vec::Vec<alloc::vec::Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl From<MetadataReceivedParams<'_, '_, '_>> for OwnedMetadataRecvdParams {
|
||||||
|
fn from(value: MetadataReceivedParams) -> Self {
|
||||||
|
Self::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl From<&MetadataReceivedParams<'_, '_, '_>> for OwnedMetadataRecvdParams {
|
||||||
|
fn from(value: &MetadataReceivedParams) -> Self {
|
||||||
|
Self {
|
||||||
|
id: value.id,
|
||||||
|
source_id: value.source_id,
|
||||||
|
file_size: value.file_size,
|
||||||
|
src_file_name: value.src_file_name.into(),
|
||||||
|
dest_file_name: value.dest_file_name.into(),
|
||||||
|
msgs_to_user: value.msgs_to_user.iter().map(|tlv| tlv.to_vec()).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FileSegmentRecvdParams<'seg_meta> {
|
pub struct FileSegmentRecvdParams<'seg_meta> {
|
||||||
pub id: TransactionId,
|
pub id: TransactionId,
|
||||||
pub offset: u64,
|
pub offset: u64,
|
||||||
pub length: usize,
|
pub length: usize,
|
||||||
pub rec_cont_state: Option<RecordContinuationState>,
|
pub segment_metadata: Option<&'seg_meta SegmentMetadata<'seg_meta>>,
|
||||||
pub segment_metadata: Option<&'seg_meta [u8]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CfdpUser {
|
pub trait CfdpUser {
|
||||||
|
@ -113,7 +113,7 @@ pub fn parse_buffer_for_ccsds_space_packets<E>(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::{tc::PusTcCreator, SerializablePusPacket},
|
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||||
PacketId, SpHeader,
|
PacketId, SpHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ mod tests {
|
|||||||
use alloc::{boxed::Box, sync::Arc};
|
use alloc::{boxed::Box, sync::Arc};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::{tc::PusTcCreator, SerializablePusPacket},
|
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||||
PacketId, SpHeader,
|
PacketId, SpHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ use std::vec::Vec;
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
/// use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
||||||
/// use spacepackets::ecss::SerializablePusPacket;
|
/// use spacepackets::ecss::WritablePusPacket;
|
||||||
/// use satrs_core::hal::std::udp_server::UdpTcServer;
|
/// use satrs_core::hal::std::udp_server::UdpTcServer;
|
||||||
/// use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
|
/// use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
|
||||||
/// use spacepackets::SpHeader;
|
/// use spacepackets::SpHeader;
|
||||||
@ -144,7 +144,7 @@ mod tests {
|
|||||||
use crate::hal::std::udp_server::{ReceiveResult, UdpTcServer};
|
use crate::hal::std::udp_server::{ReceiveResult, UdpTcServer};
|
||||||
use crate::tmtc::ReceivesTcCore;
|
use crate::tmtc::ReceivesTcCore;
|
||||||
use spacepackets::ecss::tc::PusTcCreator;
|
use spacepackets::ecss::tc::PusTcCreator;
|
||||||
use spacepackets::ecss::SerializablePusPacket;
|
use spacepackets::ecss::WritablePusPacket;
|
||||||
use spacepackets::SpHeader;
|
use spacepackets::SpHeader;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -20,6 +20,8 @@ extern crate downcast_rs;
|
|||||||
#[cfg(any(feature = "std", test))]
|
#[cfg(any(feature = "std", test))]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||||
pub mod cfdp;
|
pub mod cfdp;
|
||||||
pub mod encoding;
|
pub mod encoding;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -147,7 +147,7 @@ impl EventReporterBase {
|
|||||||
Ok(PusTmCreator::new(
|
Ok(PusTmCreator::new(
|
||||||
&mut sp_header,
|
&mut sp_header,
|
||||||
sec_header,
|
sec_header,
|
||||||
Some(&buf[0..current_idx]),
|
&buf[0..current_idx],
|
||||||
true,
|
true,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -626,7 +626,7 @@ mod tests {
|
|||||||
use crate::pool::{LocalPool, PoolCfg, PoolProvider, StoreAddr, StoreError};
|
use crate::pool::{LocalPool, PoolCfg, PoolProvider, StoreAddr, StoreError};
|
||||||
use alloc::collections::btree_map::Range;
|
use alloc::collections::btree_map::Range;
|
||||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
||||||
use spacepackets::ecss::SerializablePusPacket;
|
use spacepackets::ecss::WritablePusPacket;
|
||||||
use spacepackets::time::{cds, TimeWriter, UnixTimestamp};
|
use spacepackets::time::{cds, TimeWriter, UnixTimestamp};
|
||||||
use spacepackets::SpHeader;
|
use spacepackets::SpHeader;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -857,7 +857,7 @@ mod tests {
|
|||||||
let mut sp_header = SpHeader::tc_unseg(apid_to_set, 105, 0).unwrap();
|
let mut sp_header = SpHeader::tc_unseg(apid_to_set, 105, 0).unwrap();
|
||||||
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||||
sec_header.source_id = src_id_to_set;
|
sec_header.source_id = src_id_to_set;
|
||||||
let ping_tc = PusTcCreator::new(&mut sp_header, sec_header, None, true);
|
let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
|
||||||
let req_id = RequestId::from_tc(&ping_tc);
|
let req_id = RequestId::from_tc(&ping_tc);
|
||||||
assert_eq!(req_id.source_id(), src_id_to_set);
|
assert_eq!(req_id.source_id(), src_id_to_set);
|
||||||
assert_eq!(req_id.apid(), apid_to_set);
|
assert_eq!(req_id.apid(), apid_to_set);
|
||||||
|
@ -72,7 +72,7 @@ impl PusServiceHandler for PusService17TestHandler {
|
|||||||
// Sequence count will be handled centrally in TM funnel.
|
// Sequence count will be handled centrally in TM funnel.
|
||||||
let mut reply_header = SpHeader::tm_unseg(self.psb.tm_apid, 0, 0).unwrap();
|
let mut reply_header = SpHeader::tm_unseg(self.psb.tm_apid, 0, 0).unwrap();
|
||||||
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp);
|
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp);
|
||||||
let ping_reply = PusTmCreator::new(&mut reply_header, tc_header, None, true);
|
let ping_reply = PusTmCreator::new(&mut reply_header, tc_header, &[], true);
|
||||||
let result = self
|
let result = self
|
||||||
.psb
|
.psb
|
||||||
.tm_sender
|
.tm_sender
|
||||||
@ -118,7 +118,7 @@ mod tests {
|
|||||||
use crate::tmtc::tm_helper::SharedTmStore;
|
use crate::tmtc::tm_helper::SharedTmStore;
|
||||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
|
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
|
||||||
use spacepackets::ecss::tm::PusTmReader;
|
use spacepackets::ecss::tm::PusTmReader;
|
||||||
use spacepackets::ecss::{PusPacket, SerializablePusPacket};
|
use spacepackets::ecss::{PusPacket, WritablePusPacket};
|
||||||
use spacepackets::{SequenceFlags, SpHeader};
|
use spacepackets::{SequenceFlags, SpHeader};
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::sync::{mpsc, RwLock};
|
use std::sync::{mpsc, RwLock};
|
||||||
@ -154,7 +154,7 @@ mod tests {
|
|||||||
// Create a ping TC, verify acceptance.
|
// Create a ping TC, verify acceptance.
|
||||||
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
||||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||||
let ping_tc = PusTcCreator::new(&mut sp_header, sec_header, None, true);
|
let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
|
||||||
let token = verification_handler.add_tc(&ping_tc);
|
let token = verification_handler.add_tc(&ping_tc);
|
||||||
let token = verification_handler
|
let token = verification_handler
|
||||||
.acceptance_success(token, None)
|
.acceptance_success(token, None)
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
//!
|
//!
|
||||||
//! let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap();
|
//! let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap();
|
||||||
//! let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
//! let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||||
//! let pus_tc_0 = PusTcCreator::new(&mut sph, tc_header, None, true);
|
//! let pus_tc_0 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true);
|
||||||
//! let init_token = reporter.add_tc(&pus_tc_0);
|
//! let init_token = reporter.add_tc(&pus_tc_0);
|
||||||
//!
|
//!
|
||||||
//! // Complete success sequence for a telecommand
|
//! // Complete success sequence for a telecommand
|
||||||
@ -87,7 +87,7 @@ use delegate::delegate;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use spacepackets::ecss::tc::IsPusTelecommand;
|
use spacepackets::ecss::tc::IsPusTelecommand;
|
||||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||||
use spacepackets::ecss::{EcssEnumeration, PusError, SerializablePusPacket};
|
use spacepackets::ecss::{EcssEnumeration, PusError, WritablePusPacket};
|
||||||
use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl};
|
use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl};
|
||||||
use spacepackets::{SpHeader, MAX_APID};
|
use spacepackets::{SpHeader, MAX_APID};
|
||||||
|
|
||||||
@ -353,7 +353,7 @@ impl<'src_data, State, SuccessOrFailure> VerificationSendable<'src_data, State,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn len_packed(&self) -> usize {
|
pub fn len_packed(&self) -> usize {
|
||||||
self.pus_tm.as_ref().unwrap().len_packed()
|
self.pus_tm.as_ref().unwrap().len_written()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pus_tm(&self) -> &PusTmCreator<'src_data> {
|
pub fn pus_tm(&self) -> &PusTmCreator<'src_data> {
|
||||||
@ -877,7 +877,7 @@ impl VerificationReporterCore {
|
|||||||
PusTmCreator::new(
|
PusTmCreator::new(
|
||||||
sp_header,
|
sp_header,
|
||||||
tm_sec_header,
|
tm_sec_header,
|
||||||
Some(&src_data_buf[0..source_data_len]),
|
&src_data_buf[0..source_data_len],
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1438,6 +1438,7 @@ mod tests {
|
|||||||
fn base_tc_init(app_data: Option<&[u8]>) -> (PusTcCreator, RequestId) {
|
fn base_tc_init(app_data: Option<&[u8]>) -> (PusTcCreator, RequestId) {
|
||||||
let mut sph = SpHeader::tc_unseg(TEST_APID, 0x34, 0).unwrap();
|
let mut sph = SpHeader::tc_unseg(TEST_APID, 0x34, 0).unwrap();
|
||||||
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||||
|
let app_data = app_data.unwrap_or(&[]);
|
||||||
let pus_tc = PusTcCreator::new(&mut sph, tc_header, app_data, true);
|
let pus_tc = PusTcCreator::new(&mut sph, tc_header, app_data, true);
|
||||||
let req_id = RequestId::new(&pus_tc);
|
let req_id = RequestId::new(&pus_tc);
|
||||||
(pus_tc, req_id)
|
(pus_tc, req_id)
|
||||||
@ -2162,7 +2163,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap();
|
let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap();
|
||||||
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||||
let pus_tc_0 = PusTcCreator::new(&mut sph, tc_header, None, true);
|
let pus_tc_0 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true);
|
||||||
let init_token = reporter.add_tc(&pus_tc_0);
|
let init_token = reporter.add_tc(&pus_tc_0);
|
||||||
|
|
||||||
// Complete success sequence for a telecommand
|
// Complete success sequence for a telecommand
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
//! use satrs_core::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor};
|
//! use satrs_core::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor};
|
||||||
//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
|
//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
|
||||||
//! use spacepackets::{CcsdsPacket, SpHeader};
|
//! use spacepackets::{CcsdsPacket, SpHeader};
|
||||||
//! use spacepackets::ecss::SerializablePusPacket;
|
//! use spacepackets::ecss::WritablePusPacket;
|
||||||
//! use spacepackets::ecss::tc::{PusTc, PusTcCreator};
|
//! use spacepackets::ecss::tc::{PusTc, PusTcCreator};
|
||||||
//!
|
//!
|
||||||
//! #[derive (Default)]
|
//! #[derive (Default)]
|
||||||
@ -226,7 +226,7 @@ pub(crate) mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler};
|
use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler};
|
||||||
use spacepackets::ecss::tc::PusTcCreator;
|
use spacepackets::ecss::tc::PusTcCreator;
|
||||||
use spacepackets::ecss::SerializablePusPacket;
|
use spacepackets::ecss::WritablePusPacket;
|
||||||
use spacepackets::CcsdsPacket;
|
use spacepackets::CcsdsPacket;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@ -244,9 +244,10 @@ pub(crate) mod tests {
|
|||||||
&buf[0..size]
|
&buf[0..size]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SharedPacketQueue = Arc<Mutex<VecDeque<(u16, Vec<u8>)>>>;
|
||||||
pub struct BasicApidHandlerSharedQueue {
|
pub struct BasicApidHandlerSharedQueue {
|
||||||
pub known_packet_queue: Arc<Mutex<VecDeque<(u16, Vec<u8>)>>>,
|
pub known_packet_queue: SharedPacketQueue,
|
||||||
pub unknown_packet_queue: Arc<Mutex<VecDeque<(u16, Vec<u8>)>>>,
|
pub unknown_packet_queue: SharedPacketQueue,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -268,11 +269,11 @@ pub(crate) mod tests {
|
|||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
vec.extend_from_slice(tc_raw);
|
vec.extend_from_slice(tc_raw);
|
||||||
Ok(self
|
self.known_packet_queue
|
||||||
.known_packet_queue
|
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push_back((sp_header.apid(), vec)))
|
.push_back((sp_header.apid(), vec));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_unknown_apid(
|
fn handle_unknown_apid(
|
||||||
@ -282,11 +283,11 @@ pub(crate) mod tests {
|
|||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
vec.extend_from_slice(tc_raw);
|
vec.extend_from_slice(tc_raw);
|
||||||
Ok(self
|
self.unknown_packet_queue
|
||||||
.unknown_packet_queue
|
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push_back((sp_header.apid(), vec)))
|
.push_back((sp_header.apid(), vec));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use spacepackets::ecss::SerializablePusPacket;
|
//! use spacepackets::ecss::WritablePusPacket;
|
||||||
//! use satrs_core::tmtc::pus_distrib::{PusDistributor, PusServiceProvider};
|
//! use satrs_core::tmtc::pus_distrib::{PusDistributor, PusServiceProvider};
|
||||||
//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
|
//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
|
||||||
//! use spacepackets::SpHeader;
|
//! use spacepackets::SpHeader;
|
||||||
|
@ -11,7 +11,7 @@ pub mod std_mod {
|
|||||||
use crate::pool::{ShareablePoolProvider, SharedPool, StoreAddr};
|
use crate::pool::{ShareablePoolProvider, SharedPool, StoreAddr};
|
||||||
use crate::pus::EcssTmtcError;
|
use crate::pus::EcssTmtcError;
|
||||||
use spacepackets::ecss::tm::PusTmCreator;
|
use spacepackets::ecss::tm::PusTmCreator;
|
||||||
use spacepackets::ecss::SerializablePusPacket;
|
use spacepackets::ecss::WritablePusPacket;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -32,7 +32,7 @@ pub mod std_mod {
|
|||||||
|
|
||||||
pub fn add_pus_tm(&self, pus_tm: &PusTmCreator) -> Result<StoreAddr, EcssTmtcError> {
|
pub fn add_pus_tm(&self, pus_tm: &PusTmCreator) -> Result<StoreAddr, EcssTmtcError> {
|
||||||
let mut pg = self.pool.write().map_err(|_| EcssTmtcError::StoreLock)?;
|
let mut pg = self.pool.write().map_err(|_| EcssTmtcError::StoreLock)?;
|
||||||
let (addr, buf) = pg.free_element(pus_tm.len_packed())?;
|
let (addr, buf) = pg.free_element(pus_tm.len_written())?;
|
||||||
pus_tm
|
pus_tm
|
||||||
.write_to_bytes(buf)
|
.write_to_bytes(buf)
|
||||||
.expect("writing PUS TM to store failed");
|
.expect("writing PUS TM to store failed");
|
||||||
@ -59,7 +59,7 @@ impl PusTmWithCdsShortHelper {
|
|||||||
&'a mut self,
|
&'a mut self,
|
||||||
service: u8,
|
service: u8,
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
source_data: Option<&'a [u8]>,
|
source_data: &'a [u8],
|
||||||
seq_count: u16,
|
seq_count: u16,
|
||||||
) -> PusTmCreator {
|
) -> PusTmCreator {
|
||||||
let time_stamp = TimeProvider::from_now_with_u16_days().unwrap();
|
let time_stamp = TimeProvider::from_now_with_u16_days().unwrap();
|
||||||
@ -71,7 +71,7 @@ impl PusTmWithCdsShortHelper {
|
|||||||
&'a mut self,
|
&'a mut self,
|
||||||
service: u8,
|
service: u8,
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
source_data: Option<&'a [u8]>,
|
source_data: &'a [u8],
|
||||||
stamper: &TimeProvider,
|
stamper: &TimeProvider,
|
||||||
seq_count: u16,
|
seq_count: u16,
|
||||||
) -> PusTmCreator {
|
) -> PusTmCreator {
|
||||||
@ -83,7 +83,7 @@ impl PusTmWithCdsShortHelper {
|
|||||||
&'a self,
|
&'a self,
|
||||||
service: u8,
|
service: u8,
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
source_data: Option<&'a [u8]>,
|
source_data: &'a [u8],
|
||||||
seq_count: u16,
|
seq_count: u16,
|
||||||
) -> PusTmCreator {
|
) -> PusTmCreator {
|
||||||
let mut reply_header = SpHeader::tm_unseg(self.apid, seq_count, 0).unwrap();
|
let mut reply_header = SpHeader::tm_unseg(self.apid, seq_count, 0).unwrap();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use spacepackets::ecss::{Ptc, RealPfc, UnsignedPfc};
|
use spacepackets::ecss::{PfcReal, PfcUnsigned, Ptc};
|
||||||
use spacepackets::time::cds::TimeProvider;
|
use spacepackets::time::cds::TimeProvider;
|
||||||
use spacepackets::time::{CcsdsTimeProvider, TimeWriter};
|
use spacepackets::time::{CcsdsTimeProvider, TimeWriter};
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ impl TestMgmHkWithIndividualValidity {
|
|||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = Ptc::Real as u8;
|
buf[curr_idx] = Ptc::Real as u8;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = RealPfc::Float as u8;
|
buf[curr_idx] = PfcReal::Float as u8;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx..curr_idx + size_of::<f32>()].copy_from_slice(&self.temp.val.to_be_bytes());
|
buf[curr_idx..curr_idx + size_of::<f32>()].copy_from_slice(&self.temp.val.to_be_bytes());
|
||||||
curr_idx += size_of::<f32>();
|
curr_idx += size_of::<f32>();
|
||||||
@ -75,7 +75,7 @@ impl TestMgmHkWithIndividualValidity {
|
|||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = Ptc::UnsignedInt as u8;
|
buf[curr_idx] = Ptc::UnsignedInt as u8;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = UnsignedPfc::TwoBytes as u8;
|
buf[curr_idx] = PfcUnsigned::TwoBytes as u8;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = 3;
|
buf[curr_idx] = 3;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
@ -100,7 +100,7 @@ impl TestMgmHkWithGroupValidity {
|
|||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = Ptc::Real as u8;
|
buf[curr_idx] = Ptc::Real as u8;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = RealPfc::Float as u8;
|
buf[curr_idx] = PfcReal::Float as u8;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx..curr_idx + size_of::<f32>()].copy_from_slice(&self.temp.to_be_bytes());
|
buf[curr_idx..curr_idx + size_of::<f32>()].copy_from_slice(&self.temp.to_be_bytes());
|
||||||
curr_idx += size_of::<f32>();
|
curr_idx += size_of::<f32>();
|
||||||
@ -109,7 +109,7 @@ impl TestMgmHkWithGroupValidity {
|
|||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = Ptc::UnsignedInt as u8;
|
buf[curr_idx] = Ptc::UnsignedInt as u8;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = UnsignedPfc::TwoBytes as u8;
|
buf[curr_idx] = PfcUnsigned::TwoBytes as u8;
|
||||||
curr_idx += 1;
|
curr_idx += 1;
|
||||||
buf[curr_idx] = 3;
|
buf[curr_idx] = 3;
|
||||||
for val in self.mgm_vals {
|
for val in self.mgm_vals {
|
||||||
|
@ -9,7 +9,7 @@ pub mod crossbeam_test {
|
|||||||
use satrs_core::tmtc::tm_helper::SharedTmStore;
|
use satrs_core::tmtc::tm_helper::SharedTmStore;
|
||||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
|
||||||
use spacepackets::ecss::tm::PusTmReader;
|
use spacepackets::ecss::tm::PusTmReader;
|
||||||
use spacepackets::ecss::{EcssEnumU16, EcssEnumU8, PusPacket, SerializablePusPacket};
|
use spacepackets::ecss::{EcssEnumU16, EcssEnumU8, PusPacket, WritablePusPacket};
|
||||||
use spacepackets::SpHeader;
|
use spacepackets::SpHeader;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@ -54,17 +54,17 @@ pub mod crossbeam_test {
|
|||||||
let mut tc_guard = shared_tc_pool_0.write().unwrap();
|
let mut tc_guard = shared_tc_pool_0.write().unwrap();
|
||||||
let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap();
|
let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap();
|
||||||
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||||
let pus_tc_0 = PusTcCreator::new(&mut sph, tc_header, None, true);
|
let pus_tc_0 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true);
|
||||||
req_id_0 = RequestId::new(&pus_tc_0);
|
req_id_0 = RequestId::new(&pus_tc_0);
|
||||||
let (addr, mut buf) = tc_guard.free_element(pus_tc_0.len_packed()).unwrap();
|
let (addr, buf) = tc_guard.free_element(pus_tc_0.len_written()).unwrap();
|
||||||
pus_tc_0.write_to_bytes(&mut buf).unwrap();
|
pus_tc_0.write_to_bytes(buf).unwrap();
|
||||||
tx_tc_0.send(addr).unwrap();
|
tx_tc_0.send(addr).unwrap();
|
||||||
let mut sph = SpHeader::tc_unseg(TEST_APID, 1, 0).unwrap();
|
let mut sph = SpHeader::tc_unseg(TEST_APID, 1, 0).unwrap();
|
||||||
let tc_header = PusTcSecondaryHeader::new_simple(5, 1);
|
let tc_header = PusTcSecondaryHeader::new_simple(5, 1);
|
||||||
let pus_tc_1 = PusTcCreator::new(&mut sph, tc_header, None, true);
|
let pus_tc_1 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true);
|
||||||
req_id_1 = RequestId::new(&pus_tc_1);
|
req_id_1 = RequestId::new(&pus_tc_1);
|
||||||
let (addr, mut buf) = tc_guard.free_element(pus_tc_0.len_packed()).unwrap();
|
let (addr, buf) = tc_guard.free_element(pus_tc_0.len_written()).unwrap();
|
||||||
pus_tc_1.write_to_bytes(&mut buf).unwrap();
|
pus_tc_1.write_to_bytes(buf).unwrap();
|
||||||
tx_tc_1.send(addr).unwrap();
|
tx_tc_1.send(addr).unwrap();
|
||||||
}
|
}
|
||||||
let verif_sender_0 = thread::spawn(move || {
|
let verif_sender_0 = thread::spawn(move || {
|
||||||
@ -81,16 +81,14 @@ pub mod crossbeam_test {
|
|||||||
tc_buf[0..tc_len].copy_from_slice(buf);
|
tc_buf[0..tc_len].copy_from_slice(buf);
|
||||||
}
|
}
|
||||||
let (_tc, _) = PusTcReader::new(&tc_buf[0..tc_len]).unwrap();
|
let (_tc, _) = PusTcReader::new(&tc_buf[0..tc_len]).unwrap();
|
||||||
let accepted_token;
|
|
||||||
|
|
||||||
let token = reporter_with_sender_0.add_tc_with_req_id(req_id_0);
|
let token = reporter_with_sender_0.add_tc_with_req_id(req_id_0);
|
||||||
accepted_token = reporter_with_sender_0
|
let accepted_token = reporter_with_sender_0
|
||||||
.acceptance_success(token, Some(&FIXED_STAMP))
|
.acceptance_success(token, Some(&FIXED_STAMP))
|
||||||
.expect("Acceptance success failed");
|
.expect("Acceptance success failed");
|
||||||
|
|
||||||
// Do some start handling here
|
// Do some start handling here
|
||||||
let started_token;
|
let started_token = reporter_with_sender_0
|
||||||
started_token = reporter_with_sender_0
|
|
||||||
.start_success(accepted_token, Some(&FIXED_STAMP))
|
.start_success(accepted_token, Some(&FIXED_STAMP))
|
||||||
.expect("Start success failed");
|
.expect("Start success failed");
|
||||||
// Do some step handling here
|
// Do some step handling here
|
||||||
@ -158,8 +156,7 @@ pub mod crossbeam_test {
|
|||||||
RequestId::from_bytes(&pus_tm.source_data()[0..RequestId::SIZE_AS_BYTES])
|
RequestId::from_bytes(&pus_tm.source_data()[0..RequestId::SIZE_AS_BYTES])
|
||||||
.expect("reading request ID from PUS TM source data failed");
|
.expect("reading request ID from PUS TM source data failed");
|
||||||
if !verif_map.contains_key(&req_id) {
|
if !verif_map.contains_key(&req_id) {
|
||||||
let mut content = Vec::new();
|
let content = vec![pus_tm.subservice()];
|
||||||
content.push(pus_tm.subservice());
|
|
||||||
verif_map.insert(req_id, content);
|
verif_map.insert(req_id, content);
|
||||||
} else {
|
} else {
|
||||||
let content = verif_map.get_mut(&req_id).unwrap();
|
let content = verif_map.get_mut(&req_id).unwrap();
|
||||||
|
@ -28,7 +28,7 @@ use satrs_core::{
|
|||||||
tmtc::{ReceivesTcCore, TmPacketSourceCore},
|
tmtc::{ReceivesTcCore, TmPacketSourceCore},
|
||||||
};
|
};
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
ecss::{tc::PusTcCreator, SerializablePusPacket},
|
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||||
PacketId, SpHeader,
|
PacketId, SpHeader,
|
||||||
};
|
};
|
||||||
use std::{boxed::Box, collections::VecDeque, sync::Arc, vec::Vec};
|
use std::{boxed::Box, collections::VecDeque, sync::Arc, vec::Vec};
|
||||||
|
@ -23,5 +23,5 @@ thiserror = "1"
|
|||||||
path = "../satrs-core"
|
path = "../satrs-core"
|
||||||
|
|
||||||
[dependencies.satrs-mib]
|
[dependencies.satrs-mib]
|
||||||
version = "0.1.0-alpha.1"
|
# version = "0.1.0-alpha.1"
|
||||||
# path = "../satrs-mib"
|
path = "../satrs-mib"
|
||||||
|
@ -2,7 +2,7 @@ use satrs_core::pus::verification::RequestId;
|
|||||||
use satrs_core::spacepackets::ecss::tc::PusTcCreator;
|
use satrs_core::spacepackets::ecss::tc::PusTcCreator;
|
||||||
use satrs_core::spacepackets::ecss::tm::PusTmReader;
|
use satrs_core::spacepackets::ecss::tm::PusTmReader;
|
||||||
use satrs_core::{
|
use satrs_core::{
|
||||||
spacepackets::ecss::{PusPacket, SerializablePusPacket},
|
spacepackets::ecss::{PusPacket, WritablePusPacket},
|
||||||
spacepackets::SpHeader,
|
spacepackets::SpHeader,
|
||||||
};
|
};
|
||||||
use satrs_example::{OBSW_SERVER_ADDR, SERVER_PORT};
|
use satrs_example::{OBSW_SERVER_ADDR, SERVER_PORT};
|
||||||
|
@ -466,7 +466,7 @@ fn main() {
|
|||||||
let pus_tm = PusTmCreator::new(
|
let pus_tm = PusTmCreator::new(
|
||||||
&mut sp_header,
|
&mut sp_header,
|
||||||
sec_header,
|
sec_header,
|
||||||
Some(&buf),
|
&buf,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
let addr = aocs_tm_store
|
let addr = aocs_tm_store
|
||||||
|
@ -23,7 +23,8 @@ version = "1"
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.satrs-core]
|
[dependencies.satrs-core]
|
||||||
version = "0.1.0-alpha.1"
|
path = "../satrs-core"
|
||||||
|
# version = "0.1.0-alpha.1"
|
||||||
# git = "https://egit.irs.uni-stuttgart.de/rust/sat-rs.git"
|
# git = "https://egit.irs.uni-stuttgart.de/rust/sat-rs.git"
|
||||||
# branch = "main"
|
# branch = "main"
|
||||||
# rev = "35e1f7a983f6535c5571186e361fe101d4306b89"
|
# rev = "35e1f7a983f6535c5571186e361fe101d4306b89"
|
||||||
|
@ -20,7 +20,8 @@ quote = "1"
|
|||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
|
|
||||||
[dependencies.satrs-core]
|
[dependencies.satrs-core]
|
||||||
version = "0.1.0-alpha.1"
|
path = "../../satrs-core"
|
||||||
|
# version = "0.1.0-alpha.1"
|
||||||
# git = "https://egit.irs.uni-stuttgart.de/rust/sat-rs.git"
|
# git = "https://egit.irs.uni-stuttgart.de/rust/sat-rs.git"
|
||||||
# branch = "main"
|
# branch = "main"
|
||||||
# rev = "35e1f7a983f6535c5571186e361fe101d4306b89"
|
# rev = "35e1f7a983f6535c5571186e361fe101d4306b89"
|
||||||
|
Loading…
Reference in New Issue
Block a user