continue CFDP handlers
Some checks failed
Rust/sat-rs/pipeline/head There was a failure building this commit

This commit is contained in:
Robin Müller 2023-11-11 19:05:46 +01:00
parent 2afb3de227
commit f7f1017fed
Signed by: muellerr
GPG Key ID: A649FB78196E3849
3 changed files with 235 additions and 76 deletions

View File

@ -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"

View File

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

View File

@ -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,
}; };