continue CFDP handlers
Some checks failed
Rust/sat-rs/pipeline/head There was a failure building this commit
Some checks failed
Rust/sat-rs/pipeline/head There was a failure building this commit
This commit is contained in:
parent
2afb3de227
commit
f7f1017fed
@ -73,11 +73,11 @@ features = ["all"]
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.spacepackets]
|
[dependencies.spacepackets]
|
||||||
version = "0.7.0-beta.2"
|
# version = "0.7.0-beta.2"
|
||||||
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 = "79d26e1a6"
|
||||||
# branch = ""
|
branch = "writable_pdu_packet"
|
||||||
|
|
||||||
[dependencies.cobs]
|
[dependencies.cobs]
|
||||||
git = "https://github.com/robamu/cobs.rs.git"
|
git = "https://github.com/robamu/cobs.rs.git"
|
||||||
|
@ -9,8 +9,10 @@ use crate::cfdp::user::TransactionFinishedParams;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
user::{CfdpUser, MetadataReceivedParams},
|
user::{CfdpUser, MetadataReceivedParams},
|
||||||
PacketInfo, PacketTarget, State, TransactionId, TransactionStep, CRC_32,
|
CheckTimerCreator, PacketInfo, PacketTarget, RemoteEntityConfigProvider, State, TransactionId,
|
||||||
|
TransactionStep, CRC_32,
|
||||||
};
|
};
|
||||||
|
use alloc::boxed::Box;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
cfdp::{
|
cfdp::{
|
||||||
@ -19,10 +21,10 @@ use spacepackets::{
|
|||||||
file_data::FileDataPdu,
|
file_data::FileDataPdu,
|
||||||
finished::{DeliveryCode, FileStatus, FinishedPdu},
|
finished::{DeliveryCode, FileStatus, FinishedPdu},
|
||||||
metadata::{MetadataGenericParams, MetadataPdu},
|
metadata::{MetadataGenericParams, MetadataPdu},
|
||||||
CommonPduConfig, FileDirectiveType, PduError, PduHeader,
|
CommonPduConfig, FileDirectiveType, PduError, PduHeader, WritablePduPacket,
|
||||||
},
|
},
|
||||||
tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, TlvType},
|
tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, TlvType},
|
||||||
ConditionCode, PduType, TransmissionMode,
|
ConditionCode, PduType,
|
||||||
},
|
},
|
||||||
util::UnsignedByteField,
|
util::UnsignedByteField,
|
||||||
};
|
};
|
||||||
@ -34,6 +36,8 @@ pub struct DestinationHandler {
|
|||||||
state: State,
|
state: State,
|
||||||
tparams: TransactionParams,
|
tparams: TransactionParams,
|
||||||
packets_to_send_ctx: PacketsToSendContext,
|
packets_to_send_ctx: PacketsToSendContext,
|
||||||
|
remote_cfg_table: Box<dyn RemoteEntityConfigProvider>,
|
||||||
|
check_timer_creator: Box<dyn CheckTimerCreator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -152,25 +156,38 @@ pub enum DestError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DestinationHandler {
|
impl DestinationHandler {
|
||||||
pub fn new(id: impl Into<UnsignedByteField>) -> Self {
|
pub fn new(
|
||||||
|
entity_id: impl Into<UnsignedByteField>,
|
||||||
|
remote_cfg_table: Box<dyn RemoteEntityConfigProvider>,
|
||||||
|
check_timer_creator: Box<dyn CheckTimerCreator>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: id.into(),
|
id: entity_id.into(),
|
||||||
step: TransactionStep::Idle,
|
step: TransactionStep::Idle,
|
||||||
state: State::Idle,
|
state: State::Idle,
|
||||||
tparams: Default::default(),
|
tparams: Default::default(),
|
||||||
packets_to_send_ctx: Default::default(),
|
packets_to_send_ctx: Default::default(),
|
||||||
|
remote_cfg_table,
|
||||||
|
check_timer_creator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state_machine(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), DestError> {
|
pub fn state_machine(
|
||||||
|
&mut self,
|
||||||
|
cfdp_user: &mut impl CfdpUser,
|
||||||
|
packet_to_insert: Option<&PacketInfo>,
|
||||||
|
) -> Result<(), DestError> {
|
||||||
|
if let Some(packet) = packet_to_insert {
|
||||||
|
self.insert_packet(packet)?;
|
||||||
|
}
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Idle => todo!(),
|
State::Idle => todo!(),
|
||||||
State::BusyClass1Nacked => self.fsm_nacked(cfdp_user),
|
State::Busy => self.fsm_busy(cfdp_user),
|
||||||
State::BusyClass2Acked => todo!("acknowledged mode not implemented yet"),
|
State::Suspended => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_packet(&mut self, packet_info: &PacketInfo) -> Result<(), DestError> {
|
fn insert_packet(&mut self, packet_info: &PacketInfo) -> Result<(), DestError> {
|
||||||
if packet_info.target() != PacketTarget::DestEntity {
|
if packet_info.target() != PacketTarget::DestEntity {
|
||||||
// Unwrap is okay here, a PacketInfo for a file data PDU should always have the
|
// Unwrap is okay here, a PacketInfo for a file data PDU should always have the
|
||||||
// destination as the target.
|
// destination as the target.
|
||||||
@ -297,11 +314,7 @@ impl DestinationHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.tparams.pdu_conf.trans_mode == TransmissionMode::Unacknowledged {
|
self.state = State::Busy;
|
||||||
self.state = State::BusyClass1Nacked;
|
|
||||||
} else {
|
|
||||||
self.state = State::BusyClass2Acked;
|
|
||||||
}
|
|
||||||
self.step = TransactionStep::TransactionStart;
|
self.step = TransactionStep::TransactionStart;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -338,7 +351,7 @@ impl DestinationHandler {
|
|||||||
// TODO: Check progress, and implement transfer completion timer as specified in the
|
// TODO: Check progress, and implement transfer completion timer as specified in the
|
||||||
// standard. This timer protects against out of order arrival of packets.
|
// standard. This timer protects against out of order arrival of packets.
|
||||||
if self.tparams.tstate.progress != self.tparams.file_size() {}
|
if self.tparams.tstate.progress != self.tparams.file_size() {}
|
||||||
if self.state == State::BusyClass1Nacked {
|
if self.state == State::Busy {
|
||||||
self.step = TransactionStep::TransferCompletion;
|
self.step = TransactionStep::TransferCompletion;
|
||||||
} else {
|
} else {
|
||||||
self.step = TransactionStep::SendingAckPdu;
|
self.step = TransactionStep::SendingAckPdu;
|
||||||
@ -367,7 +380,7 @@ impl DestinationHandler {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fsm_nacked(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), DestError> {
|
fn fsm_busy(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), DestError> {
|
||||||
if self.step == TransactionStep::TransactionStart {
|
if self.step == TransactionStep::TransactionStart {
|
||||||
self.transaction_start(cfdp_user)?;
|
self.transaction_start(cfdp_user)?;
|
||||||
}
|
}
|
||||||
@ -496,7 +509,10 @@ impl DestinationHandler {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::sync::atomic::{AtomicU8, Ordering};
|
use core::{
|
||||||
|
cell::Cell,
|
||||||
|
sync::atomic::{AtomicU8, Ordering},
|
||||||
|
};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std::println;
|
use std::println;
|
||||||
use std::{env::temp_dir, fs};
|
use std::{env::temp_dir, fs};
|
||||||
@ -504,10 +520,14 @@ mod tests {
|
|||||||
use alloc::{format, string::String};
|
use alloc::{format, string::String};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use spacepackets::{
|
use spacepackets::{
|
||||||
cfdp::{lv::Lv, ChecksumType},
|
cfdp::{lv::Lv, pdu::WritablePduPacket, ChecksumType, TransmissionMode},
|
||||||
util::{UbfU16, UnsignedByteFieldU16},
|
util::{UbfU16, UnsignedByteFieldU16},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::cfdp::{
|
||||||
|
CheckTimer, CheckTimerCreator, RemoteEntityConfig, StdRemoteEntityConfigProvider,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const LOCAL_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
|
const LOCAL_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
|
||||||
@ -608,6 +628,63 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TestCheckTimer {
|
||||||
|
counter: Cell<u32>,
|
||||||
|
expiry_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheckTimer for TestCheckTimer {
|
||||||
|
fn has_expired(&self) -> bool {
|
||||||
|
let current_counter = self.counter.get();
|
||||||
|
if self.expiry_count == current_counter {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
self.counter.set(current_counter + 1);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestCheckTimer {
|
||||||
|
pub fn new(expiry_after_x_calls: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
counter: Cell::new(0),
|
||||||
|
expiry_count: expiry_after_x_calls,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestCheckTimerCreator {
|
||||||
|
expiry_counter_for_source_entity: u32,
|
||||||
|
expiry_counter_for_dest_entity: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestCheckTimerCreator {
|
||||||
|
pub fn new(
|
||||||
|
expiry_counter_for_source_entity: u32,
|
||||||
|
expiry_counter_for_dest_entity: u32,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
expiry_counter_for_source_entity,
|
||||||
|
expiry_counter_for_dest_entity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheckTimerCreator for TestCheckTimerCreator {
|
||||||
|
fn get_check_timer_provider(
|
||||||
|
&self,
|
||||||
|
_local_id: &UnsignedByteField,
|
||||||
|
_remote_id: &UnsignedByteField,
|
||||||
|
entity_type: crate::cfdp::EntityType,
|
||||||
|
) -> Box<dyn CheckTimer> {
|
||||||
|
if entity_type == crate::cfdp::EntityType::Sending {
|
||||||
|
Box::new(TestCheckTimer::new(self.expiry_counter_for_source_entity))
|
||||||
|
} else {
|
||||||
|
Box::new(TestCheckTimer::new(self.expiry_counter_for_dest_entity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn init_check(handler: &DestinationHandler) {
|
fn init_check(handler: &DestinationHandler) {
|
||||||
assert_eq!(handler.state(), State::Idle);
|
assert_eq!(handler.state(), State::Idle);
|
||||||
assert_eq!(handler.step(), TransactionStep::Idle);
|
assert_eq!(handler.step(), TransactionStep::Idle);
|
||||||
@ -626,9 +703,31 @@ mod tests {
|
|||||||
(src_path, file_path)
|
(src_path, file_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn basic_remote_cfg_table() -> StdRemoteEntityConfigProvider {
|
||||||
|
let mut table = StdRemoteEntityConfigProvider::default();
|
||||||
|
let remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
|
||||||
|
UnsignedByteFieldU16::new(1).into(),
|
||||||
|
1024,
|
||||||
|
1024,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
TransmissionMode::Unacknowledged,
|
||||||
|
ChecksumType::Crc32,
|
||||||
|
);
|
||||||
|
table.add_config(&remote_entity_cfg);
|
||||||
|
table
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_dest_handler() -> DestinationHandler {
|
||||||
|
DestinationHandler::new(
|
||||||
|
REMOTE_ID,
|
||||||
|
Box::new(basic_remote_cfg_table()),
|
||||||
|
Box::new(TestCheckTimerCreator::new(2, 2)),
|
||||||
|
)
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic() {
|
fn test_basic() {
|
||||||
let dest_handler = DestinationHandler::new(REMOTE_ID);
|
let dest_handler = default_dest_handler();
|
||||||
init_check(&dest_handler);
|
init_check(&dest_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,37 +754,20 @@ mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_metadata_pdu(
|
fn create_packet_info<'a>(
|
||||||
metadata_pdu: &MetadataPdu,
|
pdu: &'a impl WritablePduPacket,
|
||||||
buf: &mut [u8],
|
buf: &'a mut [u8],
|
||||||
dest_handler: &mut DestinationHandler,
|
) -> PacketInfo<'a> {
|
||||||
) {
|
let written_len = pdu
|
||||||
let written_len = metadata_pdu
|
|
||||||
.write_to_bytes(buf)
|
.write_to_bytes(buf)
|
||||||
.expect("writing metadata PDU failed");
|
.expect("writing metadata PDU failed");
|
||||||
let packet_info =
|
PacketInfo::new(&buf[..written_len]).expect("generating packet info failed")
|
||||||
PacketInfo::new(&buf[..written_len]).expect("generating packet info failed");
|
|
||||||
let insert_result = dest_handler.insert_packet(&packet_info);
|
|
||||||
if let Err(e) = insert_result {
|
|
||||||
panic!("insert result error: {e}");
|
|
||||||
}
|
}
|
||||||
}
|
fn create_no_error_eof(file_data: &[u8], pdu_header: &PduHeader, buf: &mut [u8]) -> EofPdu {
|
||||||
|
|
||||||
fn insert_eof_pdu(
|
|
||||||
file_data: &[u8],
|
|
||||||
pdu_header: &PduHeader,
|
|
||||||
buf: &mut [u8],
|
|
||||||
dest_handler: &mut DestinationHandler,
|
|
||||||
) {
|
|
||||||
let mut digest = CRC_32.digest();
|
let mut digest = CRC_32.digest();
|
||||||
digest.update(file_data);
|
digest.update(file_data);
|
||||||
let crc32 = digest.finalize();
|
let crc32 = digest.finalize();
|
||||||
let eof_pdu = EofPdu::new_no_error(*pdu_header, crc32, file_data.len() as u64);
|
EofPdu::new_no_error(*pdu_header, crc32, file_data.len() as u64)
|
||||||
let result = eof_pdu.write_to_bytes(buf);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
let packet_info = PacketInfo::new(&buf).expect("generating packet info failed");
|
|
||||||
let result = dest_handler.insert_packet(&packet_info);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -700,23 +782,24 @@ mod tests {
|
|||||||
expected_file_size: 0,
|
expected_file_size: 0,
|
||||||
};
|
};
|
||||||
// We treat the destination handler like it is a remote entity.
|
// We treat the destination handler like it is a remote entity.
|
||||||
let mut dest_handler = DestinationHandler::new(REMOTE_ID);
|
let mut dest_handler = default_dest_handler();
|
||||||
init_check(&dest_handler);
|
init_check(&dest_handler);
|
||||||
|
|
||||||
let seq_num = UbfU16::new(0);
|
let seq_num = UbfU16::new(0);
|
||||||
let pdu_header = create_pdu_header(seq_num);
|
let pdu_header = create_pdu_header(seq_num);
|
||||||
let metadata_pdu =
|
let metadata_pdu =
|
||||||
create_metadata_pdu(&pdu_header, src_name.as_path(), dest_name.as_path(), 0);
|
create_metadata_pdu(&pdu_header, src_name.as_path(), dest_name.as_path(), 0);
|
||||||
insert_metadata_pdu(&metadata_pdu, &mut buf, &mut dest_handler);
|
let packet_info = create_packet_info(&metadata_pdu, &mut buf);
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
panic!("dest handler fsm error: {e}");
|
panic!("dest handler fsm error: {e}");
|
||||||
}
|
}
|
||||||
assert_ne!(dest_handler.state(), State::Idle);
|
assert_ne!(dest_handler.state(), State::Idle);
|
||||||
assert_eq!(dest_handler.step(), TransactionStep::ReceivingFileDataPdus);
|
assert_eq!(dest_handler.step(), TransactionStep::ReceivingFileDataPdus);
|
||||||
|
|
||||||
insert_eof_pdu(&[], &pdu_header, &mut buf, &mut dest_handler);
|
let eof_pdu = create_no_error_eof(&[], &pdu_header, &mut buf);
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let packet_info = create_packet_info(&eof_pdu, &mut buf);
|
||||||
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert_eq!(dest_handler.state(), State::Idle);
|
assert_eq!(dest_handler.state(), State::Idle);
|
||||||
assert_eq!(dest_handler.step(), TransactionStep::Idle);
|
assert_eq!(dest_handler.step(), TransactionStep::Idle);
|
||||||
@ -740,7 +823,7 @@ mod tests {
|
|||||||
expected_file_size: file_data.len(),
|
expected_file_size: file_data.len(),
|
||||||
};
|
};
|
||||||
// We treat the destination handler like it is a remote entity.
|
// We treat the destination handler like it is a remote entity.
|
||||||
let mut dest_handler = DestinationHandler::new(REMOTE_ID);
|
let mut dest_handler = default_dest_handler();
|
||||||
init_check(&dest_handler);
|
init_check(&dest_handler);
|
||||||
|
|
||||||
let seq_num = UbfU16::new(0);
|
let seq_num = UbfU16::new(0);
|
||||||
@ -751,8 +834,8 @@ mod tests {
|
|||||||
dest_name.as_path(),
|
dest_name.as_path(),
|
||||||
file_data.len() as u64,
|
file_data.len() as u64,
|
||||||
);
|
);
|
||||||
insert_metadata_pdu(&metadata_pdu, &mut buf, &mut dest_handler);
|
let packet_info = create_packet_info(&metadata_pdu, &mut buf);
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
panic!("dest handler fsm error: {e}");
|
panic!("dest handler fsm error: {e}");
|
||||||
}
|
}
|
||||||
@ -769,11 +852,12 @@ mod tests {
|
|||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
panic!("destination handler packet insertion error: {e}");
|
panic!("destination handler packet insertion error: {e}");
|
||||||
}
|
}
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
insert_eof_pdu(file_data, &pdu_header, &mut buf, &mut dest_handler);
|
let eof_pdu = create_no_error_eof(&file_data, &pdu_header, &mut buf);
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let packet_info = create_packet_info(&eof_pdu, &mut buf);
|
||||||
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert_eq!(dest_handler.state(), State::Idle);
|
assert_eq!(dest_handler.state(), State::Idle);
|
||||||
assert_eq!(dest_handler.step(), TransactionStep::Idle);
|
assert_eq!(dest_handler.step(), TransactionStep::Idle);
|
||||||
@ -800,7 +884,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// We treat the destination handler like it is a remote entity.
|
// We treat the destination handler like it is a remote entity.
|
||||||
let mut dest_handler = DestinationHandler::new(REMOTE_ID);
|
let mut dest_handler = default_dest_handler();
|
||||||
init_check(&dest_handler);
|
init_check(&dest_handler);
|
||||||
|
|
||||||
let seq_num = UbfU16::new(0);
|
let seq_num = UbfU16::new(0);
|
||||||
@ -811,8 +895,8 @@ mod tests {
|
|||||||
dest_name.as_path(),
|
dest_name.as_path(),
|
||||||
random_data.len() as u64,
|
random_data.len() as u64,
|
||||||
);
|
);
|
||||||
insert_metadata_pdu(&metadata_pdu, &mut buf, &mut dest_handler);
|
let packet_info = create_packet_info(&metadata_pdu, &mut buf);
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
panic!("dest handler fsm error: {e}");
|
panic!("dest handler fsm error: {e}");
|
||||||
}
|
}
|
||||||
@ -835,7 +919,7 @@ mod tests {
|
|||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
panic!("destination handler packet insertion error: {e}");
|
panic!("destination handler packet insertion error: {e}");
|
||||||
}
|
}
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
// Second file data PDU
|
// Second file data PDU
|
||||||
@ -853,11 +937,12 @@ mod tests {
|
|||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
panic!("destination handler packet insertion error: {e}");
|
panic!("destination handler packet insertion error: {e}");
|
||||||
}
|
}
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
insert_eof_pdu(&random_data, &pdu_header, &mut buf, &mut dest_handler);
|
let eof_pdu = create_no_error_eof(&random_data, &pdu_header, &mut buf);
|
||||||
let result = dest_handler.state_machine(&mut test_user);
|
let packet_info = create_packet_info(&eof_pdu, &mut buf);
|
||||||
|
let result = dest_handler.state_machine(&mut test_user, Some(&packet_info));
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert_eq!(dest_handler.state(), State::Idle);
|
assert_eq!(dest_handler.state(), State::Idle);
|
||||||
assert_eq!(dest_handler.step(), TransactionStep::Idle);
|
assert_eq!(dest_handler.step(), TransactionStep::Idle);
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
use core::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},
|
||||||
@ -35,7 +38,7 @@ 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 {
|
pub trait CheckTimer {
|
||||||
fn has_expired(&self) -> bool;
|
fn has_expired(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,10 +53,11 @@ pub trait CheckTimerProvider {
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub trait CheckTimerCreator {
|
pub trait CheckTimerCreator {
|
||||||
fn get_check_timer_provider(
|
fn get_check_timer_provider(
|
||||||
|
&self,
|
||||||
local_id: &UnsignedByteField,
|
local_id: &UnsignedByteField,
|
||||||
remote_id: &UnsignedByteField,
|
remote_id: &UnsignedByteField,
|
||||||
entity_type: EntityType,
|
entity_type: EntityType,
|
||||||
) -> Box<dyn CheckTimerProvider>;
|
) -> Box<dyn CheckTimer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simple implementation of the [CheckTimerProvider] trait assuming a standard runtime.
|
/// Simple implementation of the [CheckTimerProvider] trait assuming a standard runtime.
|
||||||
@ -75,7 +79,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 {
|
||||||
@ -85,22 +89,78 @@ impl CheckTimerProvider for StdCheckTimer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct RemoteEntityConfig {
|
pub struct RemoteEntityConfig {
|
||||||
pub entity_id: UnsignedByteField,
|
pub entity_id: UnsignedByteField,
|
||||||
pub max_file_segment_len: usize,
|
pub max_file_segment_len: usize,
|
||||||
pub closure_requeted_by_default: bool,
|
pub max_packet_len: usize,
|
||||||
|
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 check_limit: u32,
|
pub check_limit: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RemoteEntityConfigProvider {
|
impl RemoteEntityConfig {
|
||||||
fn get_remote_config(&self, remote_id: &UnsignedByteField) -> Option<&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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
pub trait RemoteEntityConfigProvider {
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,6 +181,20 @@ 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 {
|
||||||
@ -136,8 +210,8 @@ pub enum TransactionStep {
|
|||||||
#[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);
|
||||||
@ -249,7 +323,7 @@ mod tests {
|
|||||||
eof::EofPdu,
|
eof::EofPdu,
|
||||||
file_data::FileDataPdu,
|
file_data::FileDataPdu,
|
||||||
metadata::{MetadataGenericParams, MetadataPdu},
|
metadata::{MetadataGenericParams, MetadataPdu},
|
||||||
CommonPduConfig, FileDirectiveType, PduHeader,
|
CommonPduConfig, FileDirectiveType, PduHeader, WritablePduPacket,
|
||||||
},
|
},
|
||||||
PduType,
|
PduType,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user