1
//! # CFDP Source Entity Module
2
//!
3
//! The [SourceHandler] is the primary component of this module which converts a
4
//! [ReadablePutRequest] into all packet data units (PDUs) which need to be sent to a remote
5
//! CFDP entity to perform a File Copy operation to a remote entity.
6
//!
7
//! The source entity allows freedom communication by using a user-provided [PduSendProvider]
8
//! to send all generated PDUs. It should be noted that for regular file transfers, each
9
//! [SourceHandler::state_machine] call will map to one generated file data PDU. This allows
10
//! flow control for the user of the state machine.
11
//!
12
//! The [SourceHandler::state_machine] will generally perform the following steps after a valid
13
//! put request was received through the [SourceHandler::put_request] method:
14
//!
15
//! 1. Generate the Metadata PDU to be sent to a remote CFDP entity. You can use the
16
//!    [spacepackets::cfdp::pdu::metadata::MetadataPduReader] to inspect the generated PDU.
17
//! 2. Generate all File Data PDUs to be sent to a remote CFDP entity if applicable (file not
18
//!    empty). The PDU(s) can be inspected using the [spacepackets::cfdp::pdu::file_data::FileDataPdu] reader.
19
//! 3. Generate an EOF PDU to be sent to a remote CFDP entity. The PDU can be inspected using
20
//!    the [spacepackets::cfdp::pdu::eof::EofPdu] reader.
21
//!
22
//! If this is an unacknowledged transfer with no transaction closure, the file transfer will be
23
//! done after these steps. In any other case:
24
//!
25
//! ### Unacknowledged transfer with requested closure
26
//!
27
//! 4. A Finished PDU will be awaited, for example one generated using
28
//!    [spacepackets::cfdp::pdu::finished::FinishedPduCreator].
29
//!
30
//! ### Acknowledged transfer (*not implemented yet*)
31
//!
32
//! 4. A EOF ACK packet will be awaited, for example one generated using
33
//!    [spacepackets::cfdp::pdu::ack::AckPdu].
34
//! 5. A Finished PDU will be awaited, for example one generated using
35
//!    [spacepackets::cfdp::pdu::finished::FinishedPduCreator].
36
//! 6. A finished PDU ACK packet will be generated to be sent to the remote CFDP entity.
37
//!    The [spacepackets::cfdp::pdu::finished::FinishedPduReader] can be used to inspect the
38
//!    generated PDU.
39
use core::{cell::RefCell, ops::ControlFlow, str::Utf8Error};
40

            
41
use spacepackets::{
42
    cfdp::{
43
        lv::Lv,
44
        pdu::{
45
            eof::EofPdu,
46
            file_data::{
47
                calculate_max_file_seg_len_for_max_packet_len_and_pdu_header,
48
                FileDataPduCreatorWithReservedDatafield,
49
            },
50
            finished::{DeliveryCode, FileStatus, FinishedPduReader},
51
            metadata::{MetadataGenericParams, MetadataPduCreator},
52
            CfdpPdu, CommonPduConfig, FileDirectiveType, PduError, PduHeader, WritablePduPacket,
53
        },
54
        ConditionCode, Direction, LargeFileFlag, PduType, SegmentMetadataFlag, SegmentationControl,
55
        TransmissionMode,
56
    },
57
    util::{UnsignedByteField, UnsignedEnum},
58
    ByteConversionError,
59
};
60

            
61
use spacepackets::seq_count::SequenceCountProvider;
62

            
63
use crate::{
64
    time::CountdownProvider, DummyPduProvider, EntityType, GenericSendError, PduProvider,
65
    TimerCreatorProvider,
66
};
67

            
68
use super::{
69
    filestore::{FilestoreError, VirtualFilestore},
70
    request::{ReadablePutRequest, StaticPutRequestCacher},
71
    user::{CfdpUser, TransactionFinishedParams},
72
    LocalEntityConfig, PacketTarget, PduSendProvider, RemoteEntityConfig,
73
    RemoteEntityConfigProvider, State, TransactionId, UserFaultHookProvider,
74
};
75

            
76
/// This enumeration models the different transaction steps of the source entity handler.
77
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
78
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
80
pub enum TransactionStep {
81
    Idle = 0,
82
    TransactionStart = 1,
83
    SendingMetadata = 3,
84
    SendingFileData = 4,
85
    /// Re-transmitting missing packets in acknowledged mode
86
    Retransmitting = 5,
87
    SendingEof = 6,
88
    WaitingForEofAck = 7,
89
    WaitingForFinished = 8,
90
    // SendingAckOfFinished = 9,
91
    NoticeOfCompletion = 10,
92
}
93

            
94
#[derive(Default)]
95
pub struct FileParams {
96
    pub progress: u64,
97
    pub segment_len: u64,
98
    pub crc32: u32,
99
    pub metadata_only: bool,
100
    pub file_size: u64,
101
    pub empty_file: bool,
102
}
103

            
104
pub struct StateHelper {
105
    state: super::State,
106
    step: TransactionStep,
107
    num_packets_ready: u32,
108
}
109

            
110
#[derive(Debug)]
111
pub struct FinishedParams {
112
    condition_code: ConditionCode,
113
    delivery_code: DeliveryCode,
114
    file_status: FileStatus,
115
}
116

            
117
#[derive(Debug, derive_new::new)]
118
pub struct TransferState {
119
    transaction_id: TransactionId,
120
    remote_cfg: RemoteEntityConfig,
121
    transmission_mode: super::TransmissionMode,
122
    closure_requested: bool,
123
    cond_code_eof: Option<ConditionCode>,
124
    finished_params: Option<FinishedParams>,
125
}
126

            
127
impl Default for StateHelper {
128
58
    fn default() -> Self {
129
58
        Self {
130
58
            state: super::State::Idle,
131
58
            step: TransactionStep::Idle,
132
58
            num_packets_ready: 0,
133
58
        }
134
58
    }
135
}
136

            
137
#[derive(Debug, thiserror::Error)]
138
pub enum SourceError {
139
    #[error("can not process packet type {pdu_type:?} with directive type {directive_type:?}")]
140
    CantProcessPacketType {
141
        pdu_type: PduType,
142
        directive_type: Option<FileDirectiveType>,
143
    },
144
    #[error("unexpected PDU")]
145
    UnexpectedPdu {
146
        pdu_type: PduType,
147
        directive_type: Option<FileDirectiveType>,
148
    },
149
    #[error("source handler is already busy with put request")]
150
    PutRequestAlreadyActive,
151
    #[error("error caching put request")]
152
    PutRequestCaching(ByteConversionError),
153
    #[error("filestore error: {0}")]
154
    FilestoreError(#[from] FilestoreError),
155
    #[error("source file does not have valid UTF8 format: {0}")]
156
    SourceFileNotValidUtf8(Utf8Error),
157
    #[error("destination file does not have valid UTF8 format: {0}")]
158
    DestFileNotValidUtf8(Utf8Error),
159
    #[error("error related to PDU creation: {0}")]
160
    Pdu(#[from] PduError),
161
    #[error("cfdp feature not implemented")]
162
    NotImplemented,
163
    #[error("issue sending PDU: {0}")]
164
    SendError(#[from] GenericSendError),
165
}
166

            
167
#[derive(Debug, thiserror::Error)]
168
pub enum PutRequestError {
169
    #[error("error caching put request: {0}")]
170
    Storage(#[from] ByteConversionError),
171
    #[error("already busy with put request")]
172
    AlreadyBusy,
173
    #[error("no remote entity configuration found for {0:?}")]
174
    NoRemoteCfgFound(UnsignedByteField),
175
    #[error("source file does not have valid UTF8 format: {0}")]
176
    SourceFileNotValidUtf8(#[from] Utf8Error),
177
    #[error("source file does not exist")]
178
    FileDoesNotExist,
179
    #[error("filestore error: {0}")]
180
    FilestoreError(#[from] FilestoreError),
181
}
182

            
183
/// This is the primary CFDP source handler. It models the CFDP source entity, which is
184
/// primarily responsible for handling put requests to send files to another CFDP destination
185
/// entity.
186
///
187
/// As such, it contains a state machine to perform all operations necessary to perform a
188
/// source-to-destination file transfer. This class uses the user provides [PduSendProvider] to
189
/// send the CFDP PDU packets generated by the state machine.
190
///
191
/// The following core functions are the primary interface:
192
///
193
/// 1. [Self::put_request] can be used to start transactions, most notably to start
194
///    and perform a Copy File procedure to send a file or to send a Proxy Put Request to request
195
///    a file.
196
/// 2. [Self::state_machine] is the primary interface to execute an
197
///    active file transfer. It generates the necessary CFDP PDUs for this process.
198
///    This method is also used to insert received packets with the appropriate destination ID
199
///    and target handler type into the state machine.
200
///
201
/// A put request will only be accepted if the handler is in the idle state.
202
///
203
/// The handler requires the [alloc] feature but will allocated all required memory on construction
204
/// time. This means that the handler is still suitable for embedded systems where run-time
205
/// allocation is prohibited. Furthermore, it uses the [VirtualFilestore] abstraction to allow
206
/// usage on systems without a [std] filesystem.
207
/// This handler does not support concurrency out of the box. Instead, if concurrent handling
208
/// is required, it is recommended to create a new handler and run all active handlers inside a
209
/// thread pool, or move the newly created handler to a new thread.
210
pub struct SourceHandler<
211
    PduSender: PduSendProvider,
212
    UserFaultHook: UserFaultHookProvider,
213
    Vfs: VirtualFilestore,
214
    RemoteCfgTable: RemoteEntityConfigProvider,
215
    TimerCreator: TimerCreatorProvider<Countdown = Countdown>,
216
    Countdown: CountdownProvider,
217
    SeqCountProvider: SequenceCountProvider,
218
> {
219
    local_cfg: LocalEntityConfig<UserFaultHook>,
220
    pdu_sender: PduSender,
221
    pdu_and_cksum_buffer: RefCell<alloc::vec::Vec<u8>>,
222
    put_request_cacher: StaticPutRequestCacher,
223
    remote_cfg_table: RemoteCfgTable,
224
    vfs: Vfs,
225
    state_helper: StateHelper,
226
    // Transfer related state information
227
    tstate: Option<TransferState>,
228
    // File specific transfer fields
229
    fparams: FileParams,
230
    // PDU configuration is cached so it can be re-used for all PDUs generated for file transfers.
231
    pdu_conf: CommonPduConfig,
232
    countdown: Option<Countdown>,
233
    timer_creator: TimerCreator,
234
    seq_count_provider: SeqCountProvider,
235
}
236

            
237
impl<
238
        PduSender: PduSendProvider,
239
        UserFaultHook: UserFaultHookProvider,
240
        Vfs: VirtualFilestore,
241
        RemoteCfgTable: RemoteEntityConfigProvider,
242
        TimerCreator: TimerCreatorProvider<Countdown = Countdown>,
243
        Countdown: CountdownProvider,
244
        SeqCountProvider: SequenceCountProvider,
245
    >
246
    SourceHandler<
247
        PduSender,
248
        UserFaultHook,
249
        Vfs,
250
        RemoteCfgTable,
251
        TimerCreator,
252
        Countdown,
253
        SeqCountProvider,
254
    >
255
{
256
    /// Creates a new instance of a source handler.
257
    ///
258
    /// # Arguments
259
    ///
260
    /// * `cfg` - The local entity configuration for this source handler.
261
    /// * `pdu_sender` - [PduSendProvider] provider used to send CFDP PDUs generated by the handler.
262
    /// * `vfs` - [VirtualFilestore] implementation used by the handler, which decouples the CFDP
263
    ///    implementation from the underlying filestore/filesystem. This allows to use this handler
264
    ///    for embedded systems where a standard runtime might not be available.
265
    /// * `put_request_cacher` - The put request cacher is used cache put requests without
266
    ///    requiring run-time allocation.
267
    /// * `pdu_and_cksum_buf_size` - The handler requires a buffer to generate PDUs and perform
268
    ///    checksum calculations. The user can specify the size of this buffer, so this should be
269
    ///    set to the maximum expected PDU size or a conservative upper bound for this size, for
270
    ///    example 2048 or 4096 bytes.
271
    /// * `remote_cfg_table` - The [RemoteEntityConfigProvider] used to look up remote
272
    ///    entities and target specific configuration for file copy operations.
273
    /// * `timer_creator` - [TimerCreatorProvider] used by the CFDP handler to generate
274
    ///    timers required by various tasks. This allows to use this handler for embedded systems
275
    ///    where the standard time APIs might not be available.
276
    /// * `seq_count_provider` - The [SequenceCountProvider] used to generate the [TransactionId]
277
    ///    which contains an incrementing counter.
278
    #[allow(clippy::too_many_arguments)]
279
28
    pub fn new(
280
28
        cfg: LocalEntityConfig<UserFaultHook>,
281
28
        pdu_sender: PduSender,
282
28
        vfs: Vfs,
283
28
        put_request_cacher: StaticPutRequestCacher,
284
28
        pdu_and_cksum_buf_size: usize,
285
28
        remote_cfg_table: RemoteCfgTable,
286
28
        timer_creator: TimerCreator,
287
28
        seq_count_provider: SeqCountProvider,
288
28
    ) -> Self {
289
28
        Self {
290
28
            local_cfg: cfg,
291
28
            remote_cfg_table,
292
28
            pdu_sender,
293
28
            pdu_and_cksum_buffer: RefCell::new(alloc::vec![0; pdu_and_cksum_buf_size]),
294
28
            vfs,
295
28
            put_request_cacher,
296
28
            state_helper: Default::default(),
297
28
            tstate: Default::default(),
298
28
            fparams: Default::default(),
299
28
            pdu_conf: Default::default(),
300
28
            countdown: None,
301
28
            timer_creator,
302
28
            seq_count_provider,
303
28
        }
304
28
    }
305

            
306
    /// Calls [Self::state_machine], without inserting a packet.
307
30
    pub fn state_machine_no_packet(
308
30
        &mut self,
309
30
        cfdp_user: &mut impl CfdpUser,
310
30
    ) -> Result<u32, SourceError> {
311
30
        self.state_machine(cfdp_user, None::<&DummyPduProvider>)
312
30
    }
313

            
314
    /// This is the core function to drive the source handler. It is also used to insert
315
    /// packets into the source handler.
316
    ///
317
    /// The state machine should either be called if a packet with the appropriate destination ID
318
    /// is received, or periodically in IDLE periods to perform all CFDP related tasks, for example
319
    /// checking for timeouts or missed file segments.
320
    ///
321
    /// The function returns the number of sent PDU packets on success.
322
62
    pub fn state_machine(
323
62
        &mut self,
324
62
        cfdp_user: &mut impl CfdpUser,
325
62
        pdu: Option<&impl PduProvider>,
326
62
    ) -> Result<u32, SourceError> {
327
62
        if let Some(packet) = pdu {
328
8
            self.insert_packet(cfdp_user, packet)?;
329
54
        }
330
62
        match self.state_helper.state {
331
            super::State::Idle => {
332
                // TODO: In acknowledged mode, add timer handling.
333
10
                Ok(0)
334
            }
335
52
            super::State::Busy => self.fsm_busy(cfdp_user, pdu),
336
            super::State::Suspended => {
337
                // There is now way to suspend the handler currently anyway.
338
                Ok(0)
339
            }
340
        }
341
62
    }
342

            
343
8
    fn insert_packet(
344
8
        &mut self,
345
8
        _cfdp_user: &mut impl CfdpUser,
346
8
        packet_to_insert: &impl PduProvider,
347
8
    ) -> Result<(), SourceError> {
348
8
        if packet_to_insert.packet_target()? != PacketTarget::SourceEntity {
349
            // Unwrap is okay here, a PacketInfo for a file data PDU should always have the
350
            // destination as the target.
351
            return Err(SourceError::CantProcessPacketType {
352
                pdu_type: packet_to_insert.pdu_type(),
353
                directive_type: packet_to_insert.file_directive_type(),
354
            });
355
8
        }
356
8
        if packet_to_insert.pdu_type() == PduType::FileData {
357
            // The [PacketInfo] API should ensure that file data PDUs can not be passed
358
            // into a source entity, so this should never happen.
359
            return Err(SourceError::UnexpectedPdu {
360
                pdu_type: PduType::FileData,
361
                directive_type: None,
362
            });
363
8
        }
364
8
        // Unwrap is okay here, the [PacketInfo] API should ensure that the directive type is
365
8
        // always a valid value.
366
8
        match packet_to_insert
367
8
            .file_directive_type()
368
8
            .expect("PDU directive type unexpectedly not set")
369
        {
370
8
            FileDirectiveType::FinishedPdu => self.handle_finished_pdu(packet_to_insert)?,
371
            FileDirectiveType::NakPdu => self.handle_nak_pdu(),
372
            FileDirectiveType::KeepAlivePdu => self.handle_keep_alive_pdu(),
373
            FileDirectiveType::AckPdu => return Err(SourceError::NotImplemented),
374
            FileDirectiveType::EofPdu
375
            | FileDirectiveType::PromptPdu
376
            | FileDirectiveType::MetadataPdu => {
377
                return Err(SourceError::CantProcessPacketType {
378
                    pdu_type: packet_to_insert.pdu_type(),
379
                    directive_type: packet_to_insert.file_directive_type(),
380
                });
381
            }
382
        }
383
8
        Ok(())
384
8
    }
385

            
386
    /// This function is used to pass a put request to the source handler, which is
387
    /// also used to start a file copy operation. As such, this function models the Put.request
388
    /// CFDP primtiive.
389

            
390
    /// Please note that the source handler can also process one put request at a time.
391
    /// The caller is responsible of creating a new source handler, one handler can only handle
392
    /// one file copy request at a time.
393
26
    pub fn put_request(
394
26
        &mut self,
395
26
        put_request: &impl ReadablePutRequest,
396
26
    ) -> Result<(), PutRequestError> {
397
26
        if self.state_helper.state != super::State::Idle {
398
            return Err(PutRequestError::AlreadyBusy);
399
26
        }
400
26
        self.put_request_cacher.set(put_request)?;
401
26
        let remote_cfg = self.remote_cfg_table.get(
402
26
            self.put_request_cacher
403
26
                .static_fields
404
26
                .destination_id
405
26
                .value_const(),
406
26
        );
407
26
        if remote_cfg.is_none() {
408
2
            return Err(PutRequestError::NoRemoteCfgFound(
409
2
                self.put_request_cacher.static_fields.destination_id,
410
2
            ));
411
24
        }
412
24
        let remote_cfg = remote_cfg.unwrap();
413
24
        self.state_helper.num_packets_ready = 0;
414
24
        let transmission_mode = if self.put_request_cacher.static_fields.trans_mode.is_some() {
415
24
            self.put_request_cacher.static_fields.trans_mode.unwrap()
416
        } else {
417
            remote_cfg.default_transmission_mode
418
        };
419
24
        let closure_requested = if self
420
24
            .put_request_cacher
421
24
            .static_fields
422
24
            .closure_requested
423
24
            .is_some()
424
        {
425
24
            self.put_request_cacher
426
24
                .static_fields
427
24
                .closure_requested
428
24
                .unwrap()
429
        } else {
430
            remote_cfg.closure_requested_by_default
431
        };
432
24
        if self.put_request_cacher.has_source_file()
433
24
            && !self.vfs.exists(self.put_request_cacher.source_file()?)?
434
        {
435
2
            return Err(PutRequestError::FileDoesNotExist);
436
22
        }
437
22

            
438
22
        let transaction_id = TransactionId::new(
439
22
            self.local_cfg().id,
440
22
            UnsignedByteField::new(
441
22
                SeqCountProvider::MAX_BIT_WIDTH / 8,
442
22
                self.seq_count_provider.get_and_increment().into(),
443
22
            ),
444
22
        );
445
22
        // Both the source entity and destination entity ID field must have the same size.
446
22
        // We use the larger of either the Put Request destination ID or the local entity ID
447
22
        // as the size for the new entity IDs.
448
22
        let larger_entity_width = core::cmp::max(
449
22
            self.local_cfg.id.size(),
450
22
            self.put_request_cacher.static_fields.destination_id.size(),
451
22
        );
452
44
        let create_id = |cached_id: &UnsignedByteField| {
453
44
            if larger_entity_width != cached_id.size() {
454
                UnsignedByteField::new(larger_entity_width, cached_id.value_const())
455
            } else {
456
44
                *cached_id
457
            }
458
44
        };
459

            
460
        // Set PDU configuration fields which are important for generating PDUs.
461
22
        self.pdu_conf
462
22
            .set_source_and_dest_id(
463
22
                create_id(&self.local_cfg.id),
464
22
                create_id(&self.put_request_cacher.static_fields.destination_id),
465
22
            )
466
22
            .unwrap();
467
22
        // Set up other PDU configuration fields.
468
22
        self.pdu_conf.direction = Direction::TowardsReceiver;
469
22
        self.pdu_conf.crc_flag = remote_cfg.crc_on_transmission_by_default.into();
470
22
        self.pdu_conf.transaction_seq_num = *transaction_id.seq_num();
471
22
        self.pdu_conf.trans_mode = transmission_mode;
472
22
        self.fparams.segment_len = self.calculate_max_file_seg_len(remote_cfg);
473
22

            
474
22
        // Set up the transfer context structure.
475
22
        self.tstate = Some(TransferState {
476
22
            transaction_id,
477
22
            remote_cfg: *remote_cfg,
478
22
            transmission_mode,
479
22
            closure_requested,
480
22
            cond_code_eof: None,
481
22
            finished_params: None,
482
22
        });
483
22
        self.state_helper.state = super::State::Busy;
484
22
        Ok(())
485
26
    }
486

            
487
    /// This functions models the Cancel.request CFDP primitive and is the recommended way to
488
    /// cancel a transaction.
489
    ///
490
    /// This method will cause a Notice of Cancellation at this entity if a transaction is active
491
    /// and the passed transaction ID matches the currently active transaction ID. Please note
492
    /// that the state machine might still be active because a cancelled transfer might still
493
    /// require some packets to be sent to the remote receiver entity.
494
    ///
495
    /// If not unexpected errors occur, this method returns [true] if the transfer was cancelled
496
    /// propery and [false] if there is no transaction active or the passed transaction ID and the
497
    /// active ID do not match.
498
4
    pub fn cancel_request(
499
4
        &mut self,
500
4
        user: &mut impl CfdpUser,
501
4
        transaction_id: &TransactionId,
502
4
    ) -> Result<bool, SourceError> {
503
4
        if self.state_helper.state == super::State::Idle {
504
            return Ok(false);
505
4
        }
506
4
        if let Some(active_id) = self.transaction_id() {
507
4
            if active_id == *transaction_id {
508
4
                self.notice_of_cancellation(user, ConditionCode::CancelRequestReceived)?;
509
4
                return Ok(true);
510
            }
511
        }
512
        Ok(false)
513
4
    }
514

            
515
52
    fn fsm_busy(
516
52
        &mut self,
517
52
        user: &mut impl CfdpUser,
518
52
        pdu: Option<&impl PduProvider>,
519
52
    ) -> Result<u32, SourceError> {
520
52
        let mut sent_packets = 0;
521
52
        if self.state_helper.step == TransactionStep::Idle {
522
20
            self.state_helper.step = TransactionStep::TransactionStart;
523
32
        }
524
52
        if self.state_helper.step == TransactionStep::TransactionStart {
525
20
            self.handle_transaction_start(user)?;
526
20
            self.state_helper.step = TransactionStep::SendingMetadata;
527
32
        }
528
52
        if self.state_helper.step == TransactionStep::SendingMetadata {
529
20
            self.prepare_and_send_metadata_pdu()?;
530
20
            self.state_helper.step = TransactionStep::SendingFileData;
531
20
            sent_packets += 1;
532
32
        }
533
52
        if self.state_helper.step == TransactionStep::SendingFileData {
534
36
            if let ControlFlow::Break(packets) = self.file_data_fsm()? {
535
18
                sent_packets += packets;
536
18
                // Exit for each file data PDU to allow flow control.
537
18
                return Ok(sent_packets);
538
18
            }
539
16
        }
540
34
        if self.state_helper.step == TransactionStep::SendingEof {
541
18
            self.eof_fsm(user)?;
542
18
            sent_packets += 1;
543
16
        }
544
34
        if self.state_helper.step == TransactionStep::WaitingForFinished {
545
18
            self.handle_wait_for_finished_pdu(user, pdu)?;
546
16
        }
547
34
        if self.state_helper.step == TransactionStep::NoticeOfCompletion {
548
16
            self.notice_of_completion(user);
549
16
            self.reset();
550
20
        }
551
34
        Ok(sent_packets)
552
52
    }
553

            
554
18
    fn handle_wait_for_finished_pdu(
555
18
        &mut self,
556
18
        user: &mut impl CfdpUser,
557
18
        packet: Option<&impl PduProvider>,
558
18
    ) -> Result<(), SourceError> {
559
18
        if let Some(packet) = packet {
560
            if let Some(FileDirectiveType::FinishedPdu) = packet.file_directive_type() {
561
                let finished_pdu = FinishedPduReader::new(packet.pdu())?;
562
                self.tstate.as_mut().unwrap().finished_params = Some(FinishedParams {
563
                    condition_code: finished_pdu.condition_code(),
564
                    delivery_code: finished_pdu.delivery_code(),
565
                    file_status: finished_pdu.file_status(),
566
                });
567
                if self.transmission_mode().unwrap() == TransmissionMode::Acknowledged {
568
                    // TODO: Ack packet handling
569
                    self.state_helper.step = TransactionStep::NoticeOfCompletion;
570
                } else {
571
                    self.state_helper.step = TransactionStep::NoticeOfCompletion;
572
                }
573
                return Ok(());
574
            }
575
18
        }
576
        // If we reach this state, countdown is definitely valid instance.
577
18
        if self.countdown.as_ref().unwrap().has_expired() {
578
2
            self.declare_fault(user, ConditionCode::CheckLimitReached)?;
579
16
        }
580
        /*
581
        def _handle_wait_for_finish(self):
582
            if (
583
                self.transmission_mode == TransmissionMode.ACKNOWLEDGED
584
                and self.__handle_retransmission()
585
            ):
586
                return
587
            if (
588
                self._inserted_pdu.pdu is None
589
                or self._inserted_pdu.pdu_directive_type is None
590
                or self._inserted_pdu.pdu_directive_type != DirectiveType.FINISHED_PDU
591
            ):
592
                if self._params.check_timer is not None:
593
                    if self._params.check_timer.timed_out():
594
                        self._declare_fault(ConditionCode.CHECK_LIMIT_REACHED)
595
                return
596
            finished_pdu = self._inserted_pdu.to_finished_pdu()
597
            self._inserted_pdu.pdu = None
598
            self._params.finished_params = finished_pdu.finished_params
599
            if self.transmission_mode == TransmissionMode.ACKNOWLEDGED:
600
                self._prepare_finished_ack_packet(finished_pdu.condition_code)
601
                self.states.step = TransactionStep.SENDING_ACK_OF_FINISHED
602
            else:
603
                self.states.step = TransactionStep.NOTICE_OF_COMPLETION
604
                */
605
18
        Ok(())
606
18
    }
607

            
608
18
    fn eof_fsm(&mut self, user: &mut impl CfdpUser) -> Result<(), SourceError> {
609
18
        let tstate = self.tstate.as_ref().unwrap();
610
18
        let checksum = self.vfs.calculate_checksum(
611
18
            self.put_request_cacher.source_file().unwrap(),
612
18
            tstate.remote_cfg.default_crc_type,
613
18
            self.fparams.file_size,
614
18
            self.pdu_and_cksum_buffer.get_mut(),
615
18
        )?;
616
18
        self.prepare_and_send_eof_pdu(user, checksum)?;
617
18
        let tstate = self.tstate.as_ref().unwrap();
618
18
        if tstate.transmission_mode == TransmissionMode::Unacknowledged {
619
18
            if tstate.closure_requested {
620
10
                self.countdown = Some(self.timer_creator.create_countdown(
621
10
                    crate::TimerContext::CheckLimit {
622
10
                        local_id: self.local_cfg.id,
623
10
                        remote_id: tstate.remote_cfg.entity_id,
624
10
                        entity_type: EntityType::Sending,
625
10
                    },
626
10
                ));
627
10
                self.state_helper.step = TransactionStep::WaitingForFinished;
628
10
            } else {
629
8
                self.state_helper.step = TransactionStep::NoticeOfCompletion;
630
8
            }
631
        } else {
632
            // TODO: Start positive ACK procedure.
633
        }
634
        /*
635
        if self.cfg.indication_cfg.eof_sent_indication_required:
636
            assert self._params.transaction_id is not None
637
            self.user.eof_sent_indication(self._params.transaction_id)
638
        if self.transmission_mode == TransmissionMode.UNACKNOWLEDGED:
639
            if self._params.closure_requested:
640
                assert self._params.remote_cfg is not None
641
                self._params.check_timer = (
642
                    self.check_timer_provider.provide_check_timer(
643
                        local_entity_id=self.cfg.local_entity_id,
644
                        remote_entity_id=self._params.remote_cfg.entity_id,
645
                        entity_type=EntityType.SENDING,
646
                    )
647
                )
648
                self.states.step = TransactionStep.WAITING_FOR_FINISHED
649
            else:
650
                self.states.step = TransactionStep.NOTICE_OF_COMPLETION
651
        else:
652
            self._start_positive_ack_procedure()
653
            */
654
18
        Ok(())
655
18
    }
656

            
657
20
    fn handle_transaction_start(
658
20
        &mut self,
659
20
        cfdp_user: &mut impl CfdpUser,
660
20
    ) -> Result<(), SourceError> {
661
20
        let tstate = self
662
20
            .tstate
663
20
            .as_ref()
664
20
            .expect("transfer state unexpectedly empty");
665
20
        if !self.put_request_cacher.has_source_file() {
666
            self.fparams.metadata_only = true;
667
        } else {
668
20
            let source_file = self
669
20
                .put_request_cacher
670
20
                .source_file()
671
20
                .map_err(SourceError::SourceFileNotValidUtf8)?;
672
20
            if !self.vfs.exists(source_file)? {
673
                return Err(SourceError::FilestoreError(
674
                    FilestoreError::FileDoesNotExist,
675
                ));
676
20
            }
677
20
            // We expect the destination file path to consist of valid UTF-8 characters as well.
678
20
            self.put_request_cacher
679
20
                .dest_file()
680
20
                .map_err(SourceError::DestFileNotValidUtf8)?;
681
20
            self.fparams.file_size = self.vfs.file_size(source_file)?;
682
20
            if self.fparams.file_size > u32::MAX as u64 {
683
                self.pdu_conf.file_flag = LargeFileFlag::Large
684
            } else {
685
20
                if self.fparams.file_size == 0 {
686
6
                    self.fparams.empty_file = true;
687
14
                }
688
20
                self.pdu_conf.file_flag = LargeFileFlag::Normal
689
            }
690
        }
691
20
        cfdp_user.transaction_indication(&tstate.transaction_id);
692
20
        Ok(())
693
20
    }
694

            
695
20
    fn prepare_and_send_metadata_pdu(&mut self) -> Result<(), SourceError> {
696
20
        let tstate = self
697
20
            .tstate
698
20
            .as_ref()
699
20
            .expect("transfer state unexpectedly empty");
700
20
        let metadata_params = MetadataGenericParams::new(
701
20
            tstate.closure_requested,
702
20
            tstate.remote_cfg.default_crc_type,
703
20
            self.fparams.file_size,
704
20
        );
705
20
        if self.fparams.metadata_only {
706
            let metadata_pdu = MetadataPduCreator::new(
707
                PduHeader::new_no_file_data(self.pdu_conf, 0),
708
                metadata_params,
709
                Lv::new_empty(),
710
                Lv::new_empty(),
711
                self.put_request_cacher.opts_slice(),
712
            );
713
            return self.pdu_send_helper(&metadata_pdu);
714
20
        }
715
20
        let metadata_pdu = MetadataPduCreator::new(
716
20
            PduHeader::new_no_file_data(self.pdu_conf, 0),
717
20
            metadata_params,
718
20
            Lv::new_from_str(self.put_request_cacher.source_file().unwrap()).unwrap(),
719
20
            Lv::new_from_str(self.put_request_cacher.dest_file().unwrap()).unwrap(),
720
20
            self.put_request_cacher.opts_slice(),
721
20
        );
722
20
        self.pdu_send_helper(&metadata_pdu)
723
20
    }
724

            
725
36
    fn file_data_fsm(&mut self) -> Result<ControlFlow<u32>, SourceError> {
726
36
        if self.transmission_mode().unwrap() == super::TransmissionMode::Acknowledged {
727
            // TODO: Handle re-transmission
728
36
        }
729
36
        if !self.fparams.metadata_only
730
36
            && self.fparams.progress < self.fparams.file_size
731
18
            && self.send_progressing_file_data_pdu()?
732
        {
733
18
            return Ok(ControlFlow::Break(1));
734
18
        }
735
18
        if self.fparams.empty_file || self.fparams.progress >= self.fparams.file_size {
736
18
            // EOF is still expected.
737
18
            self.state_helper.step = TransactionStep::SendingEof;
738
18
            self.tstate.as_mut().unwrap().cond_code_eof = Some(ConditionCode::NoError);
739
18
        } else if self.fparams.metadata_only {
740
            // Special case: Metadata Only, no EOF required.
741
            if self.tstate.as_ref().unwrap().closure_requested {
742
                self.state_helper.step = TransactionStep::WaitingForFinished;
743
            } else {
744
                self.state_helper.step = TransactionStep::NoticeOfCompletion;
745
            }
746
        }
747
18
        Ok(ControlFlow::Continue(()))
748
36
    }
749

            
750
16
    fn notice_of_completion(&mut self, cfdp_user: &mut impl CfdpUser) {
751
16
        /*
752
16
        def _notice_of_completion(self):
753
16
            if self.cfg.indication_cfg.transaction_finished_indication_required:
754
16
                assert self._params.transaction_id is not None
755
16
                # This happens for unacknowledged file copy operation with no closure.
756
16
                if self._params.finished_params is None:
757
16
                    self._params.finished_params = FinishedParams(
758
16
                        condition_code=ConditionCode.NO_ERROR,
759
16
                        delivery_code=DeliveryCode.DATA_COMPLETE,
760
16
                        file_status=FileStatus.FILE_STATUS_UNREPORTED,
761
16
                    )
762
16
                indication_params = TransactionFinishedParams(
763
16
                    transaction_id=self._params.transaction_id,
764
16
                    finished_params=self._params.finished_params,
765
16
                )
766
16
                self.user.transaction_finished_indication(indication_params)
767
16
            # Transaction finished
768
16
            self.reset()
769
16
                */
770
16
        let tstate = self.tstate.as_ref().unwrap();
771
16
        if self.local_cfg.indication_cfg.transaction_finished {
772
            // The first case happens for unacknowledged file copy operation with no closure.
773
16
            let finished_params = if tstate.finished_params.is_none() {
774
8
                TransactionFinishedParams {
775
8
                    id: tstate.transaction_id,
776
8
                    condition_code: ConditionCode::NoError,
777
8
                    delivery_code: DeliveryCode::Complete,
778
8
                    file_status: FileStatus::Unreported,
779
8
                }
780
            } else {
781
8
                let finished_params = tstate.finished_params.as_ref().unwrap();
782
8
                TransactionFinishedParams {
783
8
                    id: tstate.transaction_id,
784
8
                    condition_code: finished_params.condition_code,
785
8
                    delivery_code: finished_params.delivery_code,
786
8
                    file_status: finished_params.file_status,
787
8
                }
788
            };
789
16
            cfdp_user.transaction_finished_indication(&finished_params);
790
        }
791
16
    }
792

            
793
22
    fn calculate_max_file_seg_len(&self, remote_cfg: &RemoteEntityConfig) -> u64 {
794
22
        let mut derived_max_seg_len = calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(
795
22
            &PduHeader::new_no_file_data(self.pdu_conf, 0),
796
22
            remote_cfg.max_packet_len,
797
22
            None,
798
22
        );
799
22
        if remote_cfg.max_file_segment_len.is_some() {
800
            derived_max_seg_len = core::cmp::min(
801
                remote_cfg.max_file_segment_len.unwrap(),
802
                derived_max_seg_len,
803
            );
804
22
        }
805
22
        derived_max_seg_len as u64
806
22
    }
807

            
808
18
    fn send_progressing_file_data_pdu(&mut self) -> Result<bool, SourceError> {
809
18
        // Should never be called, but use defensive programming here.
810
18
        if self.fparams.progress >= self.fparams.file_size {
811
            return Ok(false);
812
18
        }
813
18
        let read_len = if self.fparams.file_size < self.fparams.segment_len {
814
8
            self.fparams.file_size
815
10
        } else if self.fparams.progress + self.fparams.segment_len > self.fparams.file_size {
816
4
            self.fparams.file_size - self.fparams.progress
817
        } else {
818
6
            self.fparams.segment_len
819
        };
820
18
        let pdu_creator = FileDataPduCreatorWithReservedDatafield::new_no_seg_metadata(
821
18
            PduHeader::new_for_file_data(
822
18
                self.pdu_conf,
823
18
                0,
824
18
                SegmentMetadataFlag::NotPresent,
825
18
                SegmentationControl::NoRecordBoundaryPreservation,
826
18
            ),
827
18
            self.fparams.progress,
828
18
            read_len,
829
18
        );
830
18
        let mut unwritten_pdu =
831
18
            pdu_creator.write_to_bytes_partially(self.pdu_and_cksum_buffer.get_mut())?;
832
18
        self.vfs.read_data(
833
18
            self.put_request_cacher.source_file().unwrap(),
834
18
            self.fparams.progress,
835
18
            read_len,
836
18
            unwritten_pdu.file_data_field_mut(),
837
18
        )?;
838
18
        let written_len = unwritten_pdu.finish();
839
18
        self.pdu_sender.send_pdu(
840
18
            PduType::FileData,
841
18
            None,
842
18
            &self.pdu_and_cksum_buffer.borrow()[0..written_len],
843
18
        )?;
844
18
        self.fparams.progress += read_len;
845
18
        /*
846
18
                """Generic function to prepare a file data PDU. This function can also be used to
847
18
                re-transmit file data PDUs of segments which were already sent."""
848
18
                assert self._put_req is not None
849
18
                assert self._put_req.source_file is not None
850
18
                with open(self._put_req.source_file, "rb") as of:
851
18
                    file_data = self.user.vfs.read_from_opened_file(of, offset, read_len)
852
18
                    # TODO: Support for record continuation state not implemented yet. Segment metadata
853
18
                    #       flag is therefore always set to False. Segment metadata support also omitted
854
18
                    #       for now. Implementing those generically could be done in form of a callback,
855
18
                    #       e.g. abstractmethod of this handler as a first way, another one being
856
18
                    #       to expect the user to supply some helper class to split up a file
857
18
                    fd_params = FileDataParams(
858
18
                        file_data=file_data, offset=offset, segment_metadata=None
859
18
                    )
860
18
                    file_data_pdu = FileDataPdu(
861
18
                        pdu_conf=self._params.pdu_conf, params=fd_params
862
18
                    )
863
18
                    self._add_packet_to_be_sent(file_data_pdu)
864
18
        */
865
18
        /*
866
18
        """Prepare the next file data PDU, which also progresses the file copy operation.
867
18

            
868
18
        :return: True if a packet was prepared, False if PDU handling is done and the next steps
869
18
            in the Copy File procedure can be performed
870
18
        """
871
18
        # This function should only be called if file segments still need to be sent.
872
18
        assert self._params.fp.progress < self._params.fp.file_size
873
18
        if self._params.fp.file_size < self._params.fp.segment_len:
874
18
            read_len = self._params.fp.file_size
875
18
        else:
876
18
            if (
877
18
                self._params.fp.progress + self._params.fp.segment_len
878
18
                > self._params.fp.file_size
879
18
            ):
880
18
                read_len = self._params.fp.file_size - self._params.fp.progress
881
18
            else:
882
18
                read_len = self._params.fp.segment_len
883
18
        self._prepare_file_data_pdu(self._params.fp.progress, read_len)
884
18
        self._params.fp.progress += read_len
885
18
            */
886
18
        Ok(true)
887
18
    }
888

            
889
24
    fn prepare_and_send_eof_pdu(
890
24
        &mut self,
891
24
        cfdp_user: &mut impl CfdpUser,
892
24
        checksum: u32,
893
24
    ) -> Result<(), SourceError> {
894
24
        let tstate = self
895
24
            .tstate
896
24
            .as_ref()
897
24
            .expect("transfer state unexpectedly empty");
898
24
        let eof_pdu = EofPdu::new(
899
24
            PduHeader::new_no_file_data(self.pdu_conf, 0),
900
24
            tstate.cond_code_eof.unwrap_or(ConditionCode::NoError),
901
24
            checksum,
902
24
            self.fparams.progress,
903
24
            None,
904
24
        );
905
24
        self.pdu_send_helper(&eof_pdu)?;
906
24
        if self.local_cfg.indication_cfg.eof_sent {
907
24
            cfdp_user.eof_sent_indication(&tstate.transaction_id);
908
24
        }
909
24
        Ok(())
910
24
    }
911

            
912
44
    fn pdu_send_helper(&self, pdu: &(impl WritablePduPacket + CfdpPdu)) -> Result<(), SourceError> {
913
44
        let mut pdu_buffer_mut = self.pdu_and_cksum_buffer.borrow_mut();
914
44
        let written_len = pdu.write_to_bytes(&mut pdu_buffer_mut)?;
915
44
        self.pdu_sender.send_pdu(
916
44
            pdu.pdu_type(),
917
44
            pdu.file_directive_type(),
918
44
            &pdu_buffer_mut[0..written_len],
919
44
        )?;
920
44
        Ok(())
921
44
    }
922

            
923
8
    fn handle_finished_pdu(&mut self, pdu_provider: &impl PduProvider) -> Result<(), SourceError> {
924
8
        // Ignore this packet when we are idle.
925
8
        if self.state_helper.state == State::Idle {
926
            return Ok(());
927
8
        }
928
8
        if self.state_helper.step != TransactionStep::WaitingForFinished {
929
            return Err(SourceError::UnexpectedPdu {
930
                pdu_type: PduType::FileDirective,
931
                directive_type: Some(FileDirectiveType::FinishedPdu),
932
            });
933
8
        }
934
8
        let finished_pdu = FinishedPduReader::new(pdu_provider.pdu())?;
935
        // Unwrapping should be fine here, the transfer state is valid when we are not in IDLE
936
        // mode.
937
8
        self.tstate.as_mut().unwrap().finished_params = Some(FinishedParams {
938
8
            condition_code: finished_pdu.condition_code(),
939
8
            delivery_code: finished_pdu.delivery_code(),
940
8
            file_status: finished_pdu.file_status(),
941
8
        });
942
8
        if self.tstate.as_ref().unwrap().transmission_mode == TransmissionMode::Acknowledged {
943
            // TODO: Send ACK packet here immediately and continue.
944
            //self.state_helper.step = TransactionStep::SendingAckOfFinished;
945
8
        }
946
8
        self.state_helper.step = TransactionStep::NoticeOfCompletion;
947
8

            
948
8
        /*
949
8
        if self.transmission_mode == TransmissionMode.ACKNOWLEDGED:
950
8
            self._prepare_finished_ack_packet(finished_pdu.condition_code)
951
8
            self.states.step = TransactionStep.SENDING_ACK_OF_FINISHED
952
8
        else:
953
8
            self.states.step = TransactionStep.NOTICE_OF_COMPLETION
954
8
        */
955
8
        Ok(())
956
8
    }
957

            
958
    fn handle_nak_pdu(&mut self) {}
959

            
960
    fn handle_keep_alive_pdu(&mut self) {}
961

            
962
26
    pub fn transaction_id(&self) -> Option<TransactionId> {
963
26
        self.tstate.as_ref().map(|v| v.transaction_id)
964
26
    }
965

            
966
    /// Returns the [TransmissionMode] for the active file operation.
967
    #[inline]
968
44
    pub fn transmission_mode(&self) -> Option<super::TransmissionMode> {
969
44
        self.tstate.as_ref().map(|v| v.transmission_mode)
970
44
    }
971

            
972
    /// Get the [TransactionStep], which denotes the exact step of a pending CFDP transaction when
973
    /// applicable.
974
60
    pub fn step(&self) -> TransactionStep {
975
60
        self.state_helper.step
976
60
    }
977

            
978
60
    pub fn state(&self) -> State {
979
60
        self.state_helper.state
980
60
    }
981

            
982
22
    pub fn local_cfg(&self) -> &LocalEntityConfig<UserFaultHook> {
983
22
        &self.local_cfg
984
22
    }
985

            
986
2
    fn declare_fault(
987
2
        &mut self,
988
2
        user: &mut impl CfdpUser,
989
2
        cond: ConditionCode,
990
2
    ) -> Result<(), SourceError> {
991
2
        // Need to cache those in advance, because a notice of cancellation can reset the handler.
992
2
        let transaction_id = self.tstate.as_ref().unwrap().transaction_id;
993
2
        let progress = self.fparams.progress;
994
2
        let fh = self.local_cfg.fault_handler.get_fault_handler(cond);
995
2
        match fh {
996
            spacepackets::cfdp::FaultHandlerCode::NoticeOfCancellation => {
997
2
                if let ControlFlow::Break(_) = self.notice_of_cancellation(user, cond)? {
998
                    return Ok(());
999
2
                }
            }
            spacepackets::cfdp::FaultHandlerCode::NoticeOfSuspension => {
                self.notice_of_suspension();
            }
            spacepackets::cfdp::FaultHandlerCode::IgnoreError => (),
            spacepackets::cfdp::FaultHandlerCode::AbandonTransaction => self.abandon_transaction(),
        }
2
        self.local_cfg
2
            .fault_handler
2
            .report_fault(transaction_id, cond, progress);
2
        Ok(())
2
    }
6
    fn notice_of_cancellation(
6
        &mut self,
6
        user: &mut impl CfdpUser,
6
        condition_code: ConditionCode,
6
    ) -> Result<ControlFlow<()>, SourceError> {
6
        let transaction_id = self.tstate.as_ref().unwrap().transaction_id;
        // CFDP standard 4.11.2.2.3: Any fault declared in the course of transferring
        // the EOF (cancel) PDU must result in abandonment of the transaction.
6
        if let Some(cond_code_eof) = self.tstate.as_ref().unwrap().cond_code_eof {
2
            if cond_code_eof != ConditionCode::NoError {
                // Still call the abandonment callback to ensure the fault is logged.
                self.local_cfg
                    .fault_handler
                    .user_hook
                    .get_mut()
                    .abandoned_cb(transaction_id, cond_code_eof, self.fparams.progress);
                self.abandon_transaction();
                return Ok(ControlFlow::Break(()));
2
            }
4
        }
6
        let tstate = self.tstate.as_mut().unwrap();
6
        tstate.cond_code_eof = Some(condition_code);
        // As specified in 4.11.2.2, prepare an EOF PDU to be sent to the remote entity. Supply
        // the checksum for the file copy progress sent so far.
6
        let checksum = self.vfs.calculate_checksum(
6
            self.put_request_cacher.source_file().unwrap(),
6
            tstate.remote_cfg.default_crc_type,
6
            self.fparams.progress,
6
            self.pdu_and_cksum_buffer.get_mut(),
6
        )?;
6
        self.prepare_and_send_eof_pdu(user, checksum)?;
6
        if self.transmission_mode().unwrap() == TransmissionMode::Unacknowledged {
6
            // We are done.
6
            self.reset();
6
        } else {
            self.state_helper.step = TransactionStep::WaitingForEofAck;
        }
6
        Ok(ControlFlow::Continue(()))
6
    }
    fn notice_of_suspension(&mut self) {}
    fn abandon_transaction(&mut self) {
        // I guess an abandoned transaction just stops whatever the handler is doing and resets
        // it to a clean state.. The implementation for this is quite easy.
        self.reset();
    }
    /*
    def _notice_of_cancellation(self, condition_code: ConditionCode) -> bool:
        """Returns whether the fault declaration handler can returns prematurely."""
        # CFDP standard 4.11.2.2.3: Any fault declared in the course of transferring
        # the EOF (cancel) PDU must result in abandonment of the transaction.
        if (
            self._params.cond_code_eof is not None
            and self._params.cond_code_eof != ConditionCode.NO_ERROR
        ):
            assert self._params.transaction_id is not None
            # We still call the abandonment callback to ensure the fault is logged.
            self.cfg.default_fault_handlers.abandoned_cb(
                self._params.transaction_id,
                self._params.cond_code_eof,
                self._params.fp.progress,
            )
            self._abandon_transaction()
            return False
        self._params.cond_code_eof = condition_code
        # As specified in 4.11.2.2, prepare an EOF PDU to be sent to the remote entity. Supply
        # the checksum for the file copy progress sent so far.
        self._prepare_eof_pdu(self._checksum_calculation(self._params.fp.progress))
        self.states.step = TransactionStep.SENDING_EOF
        return True
    */
    /// This function is public to allow completely resetting the handler, but it is explicitely
    /// discouraged to do this. CFDP has mechanism to detect issues and errors on itself.
    /// Resetting the handler might interfere with these mechanisms and lead to unexpected
    /// behaviour.
22
    pub fn reset(&mut self) {
22
        self.state_helper = Default::default();
22
        self.tstate = None;
22
        self.fparams = Default::default();
22
        self.countdown = None;
22
    }
}
#[cfg(test)]
mod tests {
    use core::time::Duration;
    use std::{fs::OpenOptions, io::Write, path::PathBuf, thread, vec::Vec};
    use alloc::string::String;
    use rand::Rng;
    use spacepackets::{
        cfdp::{
            pdu::{
                file_data::FileDataPdu, finished::FinishedPduCreator, metadata::MetadataPduReader,
            },
            ChecksumType, CrcFlag,
        },
        util::UnsignedByteFieldU16,
    };
    use tempfile::TempPath;
    use super::*;
    use crate::{
        filestore::NativeFilestore,
        request::PutRequestOwned,
        source::TransactionStep,
        tests::{basic_remote_cfg_table, SentPdu, TestCfdpSender, TestCfdpUser, TestFaultHandler},
        FaultHandler, IndicationConfig, PduRawWithInfo, StdCountdown,
        StdRemoteEntityConfigProvider, StdTimerCreator, CRC_32,
    };
    use spacepackets::seq_count::SeqCountProviderSimple;
    const LOCAL_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
    const REMOTE_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(2);
    const INVALID_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(5);
26
    fn init_full_filepaths_textfile() -> (TempPath, PathBuf) {
26
        (
26
            tempfile::NamedTempFile::new().unwrap().into_temp_path(),
26
            tempfile::TempPath::from_path("/tmp/test.txt").to_path_buf(),
26
        )
26
    }
    type TestSourceHandler = SourceHandler<
        TestCfdpSender,
        TestFaultHandler,
        NativeFilestore,
        StdRemoteEntityConfigProvider,
        StdTimerCreator,
        StdCountdown,
        SeqCountProviderSimple<u16>,
    >;
    struct SourceHandlerTestbench {
        handler: TestSourceHandler,
        #[allow(dead_code)]
        srcfile_handle: TempPath,
        srcfile: String,
        destfile: String,
        max_packet_len: usize,
        check_idle_on_drop: bool,
    }
    #[allow(dead_code)]
    struct TransferInfo {
        id: TransactionId,
        closure_requested: bool,
        pdu_header: PduHeader,
    }
    impl SourceHandlerTestbench {
24
        fn new(
24
            crc_on_transmission_by_default: bool,
24
            test_fault_handler: TestFaultHandler,
24
            test_packet_sender: TestCfdpSender,
24
            max_packet_len: usize,
24
        ) -> Self {
24
            let local_entity_cfg = LocalEntityConfig {
24
                id: LOCAL_ID.into(),
24
                indication_cfg: IndicationConfig::default(),
24
                fault_handler: FaultHandler::new(test_fault_handler),
24
            };
24
            let static_put_request_cacher = StaticPutRequestCacher::new(2048);
24
            let (srcfile_handle, destfile) = init_full_filepaths_textfile();
24
            let srcfile = String::from(srcfile_handle.to_path_buf().to_str().unwrap());
24
            Self {
24
                handler: SourceHandler::new(
24
                    local_entity_cfg,
24
                    test_packet_sender,
24
                    NativeFilestore::default(),
24
                    static_put_request_cacher,
24
                    1024,
24
                    basic_remote_cfg_table(
24
                        REMOTE_ID,
24
                        max_packet_len,
24
                        crc_on_transmission_by_default,
24
                    ),
24
                    StdTimerCreator::new(core::time::Duration::from_millis(100)),
24
                    SeqCountProviderSimple::default(),
24
                ),
24
                srcfile_handle,
24
                srcfile,
24
                destfile: String::from(destfile.to_path_buf().to_str().unwrap()),
24
                max_packet_len,
24
                check_idle_on_drop: true,
24
            }
24
        }
10
        fn create_user(&self, next_expected_seq_num: u64, filesize: u64) -> TestCfdpUser {
10
            TestCfdpUser::new(
10
                next_expected_seq_num,
10
                self.srcfile.clone(),
10
                self.destfile.clone(),
10
                filesize,
10
            )
10
        }
2
        fn set_check_limit_timeout(&mut self, timeout: Duration) {
2
            self.handler.timer_creator.check_limit_timeout = timeout;
2
        }
20
        fn put_request(
20
            &mut self,
20
            put_request: &impl ReadablePutRequest,
20
        ) -> Result<(), PutRequestError> {
20
            self.handler.put_request(put_request)
20
        }
38
        fn all_fault_queues_empty(&self) -> bool {
38
            self.handler
38
                .local_cfg
38
                .user_fault_hook()
38
                .borrow()
38
                .all_queues_empty()
38
        }
        #[allow(dead_code)]
        fn test_fault_handler(&self) -> &RefCell<TestFaultHandler> {
            self.handler.local_cfg.user_fault_hook()
        }
2
        fn test_fault_handler_mut(&mut self) -> &mut RefCell<TestFaultHandler> {
2
            self.handler.local_cfg.user_fault_hook_mut()
2
        }
34
        fn pdu_queue_empty(&self) -> bool {
34
            self.handler.pdu_sender.queue_empty()
34
        }
52
        fn get_next_sent_pdu(&self) -> Option<SentPdu> {
52
            self.handler.pdu_sender.retrieve_next_pdu()
52
        }
34
        fn common_pdu_check_for_file_transfer(&self, pdu_header: &PduHeader, crc_flag: CrcFlag) {
34
            assert_eq!(
34
                pdu_header.seg_ctrl(),
34
                SegmentationControl::NoRecordBoundaryPreservation
34
            );
34
            assert_eq!(
34
                pdu_header.seg_metadata_flag(),
34
                SegmentMetadataFlag::NotPresent
34
            );
34
            assert_eq!(pdu_header.common_pdu_conf().source_id(), LOCAL_ID.into());
34
            assert_eq!(pdu_header.common_pdu_conf().dest_id(), REMOTE_ID.into());
34
            assert_eq!(pdu_header.common_pdu_conf().crc_flag, crc_flag);
34
            assert_eq!(
34
                pdu_header.common_pdu_conf().trans_mode,
34
                TransmissionMode::Unacknowledged
34
            );
34
            assert_eq!(
34
                pdu_header.common_pdu_conf().direction,
34
                Direction::TowardsReceiver
34
            );
34
            assert_eq!(
34
                pdu_header.common_pdu_conf().file_flag,
34
                LargeFileFlag::Normal
34
            );
34
            assert_eq!(pdu_header.common_pdu_conf().transaction_seq_num.size(), 2);
34
        }
8
        fn generic_file_transfer(
8
            &mut self,
8
            cfdp_user: &mut TestCfdpUser,
8
            with_closure: bool,
8
            file_data: Vec<u8>,
8
        ) -> (PduHeader, u32) {
8
            let mut digest = CRC_32.digest();
8
            digest.update(&file_data);
8
            let checksum = digest.finalize();
8
            cfdp_user.expected_full_src_name = self.srcfile.clone();
8
            cfdp_user.expected_full_dest_name = self.destfile.clone();
8
            cfdp_user.expected_file_size = file_data.len() as u64;
8
            let put_request = PutRequestOwned::new_regular_request(
8
                REMOTE_ID.into(),
8
                &self.srcfile,
8
                &self.destfile,
8
                Some(TransmissionMode::Unacknowledged),
8
                Some(with_closure),
8
            )
8
            .expect("creating put request failed");
8
            let transaction_info = self.common_no_acked_file_transfer(
8
                cfdp_user,
8
                put_request,
8
                cfdp_user.expected_file_size,
8
            );
8
            let mut current_offset = 0;
8
            let chunks = file_data.chunks(
8
                calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(
8
                    &transaction_info.pdu_header,
8
                    self.max_packet_len,
8
                    None,
8
                ),
8
            );
8
            let mut fd_pdus = 0;
20
            for segment in chunks {
12
                self.check_next_file_pdu(current_offset, segment);
12
                self.handler.state_machine_no_packet(cfdp_user).unwrap();
12
                fd_pdus += 1;
12
                current_offset += segment.len() as u64;
12
            }
8
            self.common_eof_pdu_check(
8
                cfdp_user,
8
                transaction_info.closure_requested,
8
                cfdp_user.expected_file_size,
8
                checksum,
8
            );
8
            (transaction_info.pdu_header, fd_pdus)
8
        }
16
        fn common_no_acked_file_transfer(
16
            &mut self,
16
            cfdp_user: &mut TestCfdpUser,
16
            put_request: PutRequestOwned,
16
            filesize: u64,
16
        ) -> TransferInfo {
16
            assert_eq!(cfdp_user.transaction_indication_call_count, 0);
16
            assert_eq!(cfdp_user.eof_sent_call_count, 0);
16
            self.put_request(&put_request)
16
                .expect("put_request call failed");
16
            assert_eq!(self.handler.state(), State::Busy);
16
            assert_eq!(self.handler.step(), TransactionStep::Idle);
16
            let id = self.handler.transaction_id().unwrap();
16
            let sent_packets = self
16
                .handler
16
                .state_machine_no_packet(cfdp_user)
16
                .expect("source handler FSM failure");
16
            assert_eq!(sent_packets, 2);
16
            assert!(!self.pdu_queue_empty());
16
            let next_pdu = self.get_next_sent_pdu().unwrap();
16
            assert!(!self.pdu_queue_empty());
16
            assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
16
            assert_eq!(
16
                next_pdu.file_directive_type,
16
                Some(FileDirectiveType::MetadataPdu)
16
            );
16
            let metadata_pdu =
16
                MetadataPduReader::new(&next_pdu.raw_pdu).expect("invalid metadata PDU format");
16
            let pdu_header = metadata_pdu.pdu_header();
16
            self.common_pdu_check_for_file_transfer(metadata_pdu.pdu_header(), CrcFlag::NoCrc);
16
            assert_eq!(
16
                metadata_pdu
16
                    .src_file_name()
16
                    .value_as_str()
16
                    .unwrap()
16
                    .unwrap(),
16
                self.srcfile
16
            );
16
            assert_eq!(
16
                metadata_pdu
16
                    .dest_file_name()
16
                    .value_as_str()
16
                    .unwrap()
16
                    .unwrap(),
16
                self.destfile
16
            );
16
            assert_eq!(metadata_pdu.metadata_params().file_size, filesize);
16
            assert_eq!(
16
                metadata_pdu.metadata_params().checksum_type,
16
                ChecksumType::Crc32
16
            );
16
            let closure_requested = if let Some(closure_requested) = put_request.closure_requested {
16
                assert_eq!(
16
                    metadata_pdu.metadata_params().closure_requested,
16
                    closure_requested
16
                );
16
                closure_requested
            } else {
                assert!(metadata_pdu.metadata_params().closure_requested);
                metadata_pdu.metadata_params().closure_requested
            };
16
            assert_eq!(metadata_pdu.options(), &[]);
16
            TransferInfo {
16
                pdu_header: *pdu_header,
16
                closure_requested,
16
                id,
16
            }
16
        }
12
        fn check_next_file_pdu(&mut self, expected_offset: u64, expected_data: &[u8]) {
12
            let next_pdu = self.get_next_sent_pdu().unwrap();
12
            assert_eq!(next_pdu.pdu_type, PduType::FileData);
12
            assert!(next_pdu.file_directive_type.is_none());
12
            let fd_pdu =
12
                FileDataPdu::from_bytes(&next_pdu.raw_pdu).expect("reading file data PDU failed");
12
            assert_eq!(fd_pdu.offset(), expected_offset);
12
            assert_eq!(fd_pdu.file_data(), expected_data);
12
            assert!(fd_pdu.segment_metadata().is_none());
12
        }
14
        fn common_eof_pdu_check(
14
            &mut self,
14
            cfdp_user: &mut TestCfdpUser,
14
            closure_requested: bool,
14
            filesize: u64,
14
            checksum: u32,
14
        ) {
14
            let next_pdu = self.get_next_sent_pdu().unwrap();
14
            assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
14
            assert_eq!(
14
                next_pdu.file_directive_type,
14
                Some(FileDirectiveType::EofPdu)
14
            );
14
            let eof_pdu = EofPdu::from_bytes(&next_pdu.raw_pdu).expect("invalid EOF PDU format");
14
            self.common_pdu_check_for_file_transfer(eof_pdu.pdu_header(), CrcFlag::NoCrc);
14
            assert_eq!(eof_pdu.condition_code(), ConditionCode::NoError);
14
            assert_eq!(eof_pdu.file_size(), filesize);
14
            assert_eq!(eof_pdu.file_checksum(), checksum);
14
            assert_eq!(
14
                eof_pdu
14
                    .pdu_header()
14
                    .common_pdu_conf()
14
                    .transaction_seq_num
14
                    .value_const(),
14
                0
14
            );
14
            if !closure_requested {
6
                assert_eq!(self.handler.state(), State::Idle);
6
                assert_eq!(self.handler.step(), TransactionStep::Idle);
            } else {
8
                assert_eq!(self.handler.state(), State::Busy);
8
                assert_eq!(self.handler.step(), TransactionStep::WaitingForFinished);
            }
14
            assert_eq!(cfdp_user.transaction_indication_call_count, 1);
14
            assert_eq!(cfdp_user.eof_sent_call_count, 1);
14
            self.all_fault_queues_empty();
14
        }
4
        fn common_tiny_file_transfer(
4
            &mut self,
4
            cfdp_user: &mut TestCfdpUser,
4
            with_closure: bool,
4
        ) -> PduHeader {
4
            let mut file = OpenOptions::new()
4
                .write(true)
4
                .open(&self.srcfile)
4
                .expect("opening file failed");
4
            let content_str = "Hello World!";
4
            file.write_all(content_str.as_bytes())
4
                .expect("writing file content failed");
4
            drop(file);
4
            let (pdu_header, fd_pdus) = self.generic_file_transfer(
4
                cfdp_user,
4
                with_closure,
4
                content_str.as_bytes().to_vec(),
4
            );
4
            assert_eq!(fd_pdus, 1);
4
            pdu_header
4
        }
6
        fn finish_handling(&mut self, user: &mut TestCfdpUser, pdu_header: PduHeader) {
6
            let finished_pdu = FinishedPduCreator::new_default(
6
                pdu_header,
6
                DeliveryCode::Complete,
6
                FileStatus::Retained,
6
            );
6
            let finished_pdu_vec = finished_pdu.to_vec().unwrap();
6
            let packet_info = PduRawWithInfo::new(&finished_pdu_vec).unwrap();
6
            self.handler
6
                .state_machine(user, Some(&packet_info))
6
                .unwrap();
6
        }
    }
    impl Drop for SourceHandlerTestbench {
24
        fn drop(&mut self) {
24
            self.all_fault_queues_empty();
24
            if self.check_idle_on_drop {
24
                assert_eq!(self.handler.state(), State::Idle);
24
                assert_eq!(self.handler.step(), TransactionStep::Idle);
            }
24
        }
    }
    #[test]
2
    fn test_basic() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2
        assert!(tb.handler.transmission_mode().is_none());
2
        assert!(tb.pdu_queue_empty());
2
    }
    #[test]
2
    fn test_empty_file_transfer_not_acked_no_closure() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2
        let filesize = 0;
2
        let put_request = PutRequestOwned::new_regular_request(
2
            REMOTE_ID.into(),
2
            &tb.srcfile,
2
            &tb.destfile,
2
            Some(TransmissionMode::Unacknowledged),
2
            Some(false),
2
        )
2
        .expect("creating put request failed");
2
        let mut cfdp_user = tb.create_user(0, filesize);
2
        let transaction_info =
2
            tb.common_no_acked_file_transfer(&mut cfdp_user, put_request, filesize);
2
        tb.common_eof_pdu_check(
2
            &mut cfdp_user,
2
            transaction_info.closure_requested,
2
            filesize,
2
            CRC_32.digest().finalize(),
2
        )
2
    }
    #[test]
2
    fn test_tiny_file_transfer_not_acked_no_closure() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut cfdp_user = TestCfdpUser::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2
        tb.common_tiny_file_transfer(&mut cfdp_user, false);
2
    }
    #[test]
2
    fn test_tiny_file_transfer_not_acked_with_closure() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2
        let mut cfdp_user = TestCfdpUser::default();
2
        let pdu_header = tb.common_tiny_file_transfer(&mut cfdp_user, true);
2
        tb.finish_handling(&mut cfdp_user, pdu_header)
2
    }
    #[test]
2
    fn test_two_segment_file_transfer_not_acked_no_closure() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 128);
2
        let mut cfdp_user = TestCfdpUser::default();
2
        let mut file = OpenOptions::new()
2
            .write(true)
2
            .open(&tb.srcfile)
2
            .expect("opening file failed");
2
        let mut rand_data = [0u8; 140];
2
        rand::thread_rng().fill(&mut rand_data[..]);
2
        file.write_all(&rand_data)
2
            .expect("writing file content failed");
2
        drop(file);
2
        let (_, fd_pdus) = tb.generic_file_transfer(&mut cfdp_user, false, rand_data.to_vec());
2
        assert_eq!(fd_pdus, 2);
2
    }
    #[test]
2
    fn test_two_segment_file_transfer_not_acked_with_closure() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 128);
2
        let mut cfdp_user = TestCfdpUser::default();
2
        let mut file = OpenOptions::new()
2
            .write(true)
2
            .open(&tb.srcfile)
2
            .expect("opening file failed");
2
        let mut rand_data = [0u8; 140];
2
        rand::thread_rng().fill(&mut rand_data[..]);
2
        file.write_all(&rand_data)
2
            .expect("writing file content failed");
2
        drop(file);
2
        let (pdu_header, fd_pdus) =
2
            tb.generic_file_transfer(&mut cfdp_user, true, rand_data.to_vec());
2
        assert_eq!(fd_pdus, 2);
2
        tb.finish_handling(&mut cfdp_user, pdu_header)
2
    }
    #[test]
2
    fn test_empty_file_transfer_not_acked_with_closure() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2
        let filesize = 0;
2
        let put_request = PutRequestOwned::new_regular_request(
2
            REMOTE_ID.into(),
2
            &tb.srcfile,
2
            &tb.destfile,
2
            Some(TransmissionMode::Unacknowledged),
2
            Some(true),
2
        )
2
        .expect("creating put request failed");
2
        let mut cfdp_user = tb.create_user(0, filesize);
2
        let transaction_info =
2
            tb.common_no_acked_file_transfer(&mut cfdp_user, put_request, filesize);
2
        tb.common_eof_pdu_check(
2
            &mut cfdp_user,
2
            transaction_info.closure_requested,
2
            filesize,
2
            CRC_32.digest().finalize(),
2
        );
2
        tb.finish_handling(&mut cfdp_user, transaction_info.pdu_header)
2
    }
    #[test]
2
    fn test_put_request_no_remote_cfg() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2

            
2
        let (srcfile, destfile) = init_full_filepaths_textfile();
2
        let srcfile_str = String::from(srcfile.to_str().unwrap());
2
        let destfile_str = String::from(destfile.to_str().unwrap());
2
        let put_request = PutRequestOwned::new_regular_request(
2
            INVALID_ID.into(),
2
            &srcfile_str,
2
            &destfile_str,
2
            Some(TransmissionMode::Unacknowledged),
2
            Some(true),
2
        )
2
        .expect("creating put request failed");
2
        let error = tb.handler.put_request(&put_request);
2
        assert!(error.is_err());
2
        let error = error.unwrap_err();
2
        if let PutRequestError::NoRemoteCfgFound(id) = error {
2
            assert_eq!(id, INVALID_ID.into());
        } else {
            panic!("unexpected error type: {:?}", error);
        }
2
    }
    #[test]
2
    fn test_put_request_file_does_not_exist() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2

            
2
        let file_which_does_not_exist = "/tmp/this_file_does_not_exist.txt";
2
        let destfile = "/tmp/tmp.txt";
2
        let put_request = PutRequestOwned::new_regular_request(
2
            REMOTE_ID.into(),
2
            file_which_does_not_exist,
2
            destfile,
2
            Some(TransmissionMode::Unacknowledged),
2
            Some(true),
2
        )
2
        .expect("creating put request failed");
2
        let error = tb.put_request(&put_request);
2
        assert!(error.is_err());
2
        let error = error.unwrap_err();
2
        if !matches!(error, PutRequestError::FileDoesNotExist) {
            panic!("unexpected error type: {:?}", error);
2
        }
2
    }
    #[test]
2
    fn test_finished_pdu_check_timeout() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2
        tb.set_check_limit_timeout(Duration::from_millis(45));
2
        let filesize = 0;
2
        let put_request = PutRequestOwned::new_regular_request(
2
            REMOTE_ID.into(),
2
            &tb.srcfile,
2
            &tb.destfile,
2
            Some(TransmissionMode::Unacknowledged),
2
            Some(true),
2
        )
2
        .expect("creating put request failed");
2
        let mut cfdp_user = tb.create_user(0, filesize);
2
        let transaction_info =
2
            tb.common_no_acked_file_transfer(&mut cfdp_user, put_request, filesize);
2
        let expected_id = tb.handler.transaction_id().unwrap();
2
        tb.common_eof_pdu_check(
2
            &mut cfdp_user,
2
            transaction_info.closure_requested,
2
            filesize,
2
            CRC_32.digest().finalize(),
2
        );
2
        // After 50 ms delay, we run into a timeout, which leads to a check limit error
2
        // declaration -> leads to a notice of cancellation -> leads to an EOF PDU with the
2
        // appropriate error code.
2
        thread::sleep(Duration::from_millis(50));
2
        assert_eq!(
2
            tb.handler.state_machine_no_packet(&mut cfdp_user).unwrap(),
2
            0
2
        );
2
        let next_pdu = tb.get_next_sent_pdu().unwrap();
2
        let eof_pdu = EofPdu::from_bytes(&next_pdu.raw_pdu).expect("invalid EOF PDU format");
2
        tb.common_pdu_check_for_file_transfer(eof_pdu.pdu_header(), CrcFlag::NoCrc);
2
        assert_eq!(eof_pdu.condition_code(), ConditionCode::CheckLimitReached);
2
        assert_eq!(eof_pdu.file_size(), 0);
2
        assert_eq!(eof_pdu.file_checksum(), 0);
        // Cancellation fault should have been triggered.
2
        let fault_handler = tb.test_fault_handler_mut();
2
        let fh_ref_mut = fault_handler.get_mut();
2
        assert!(!fh_ref_mut.cancellation_queue_empty());
2
        assert_eq!(fh_ref_mut.notice_of_cancellation_queue.len(), 1);
2
        let (id, cond_code, progress) = fh_ref_mut.notice_of_cancellation_queue.pop_back().unwrap();
2
        assert_eq!(id, expected_id);
2
        assert_eq!(cond_code, ConditionCode::CheckLimitReached);
2
        assert_eq!(progress, 0);
2
        fh_ref_mut.all_queues_empty();
2
    }
    #[test]
2
    fn test_cancelled_transfer_empty_file() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512);
2
        let filesize = 0;
2
        let put_request = PutRequestOwned::new_regular_request(
2
            REMOTE_ID.into(),
2
            &tb.srcfile,
2
            &tb.destfile,
2
            Some(TransmissionMode::Unacknowledged),
2
            Some(false),
2
        )
2
        .expect("creating put request failed");
2
        let mut cfdp_user = tb.create_user(0, filesize);
2
        assert_eq!(cfdp_user.transaction_indication_call_count, 0);
2
        assert_eq!(cfdp_user.eof_sent_call_count, 0);
2
        tb.put_request(&put_request)
2
            .expect("put_request call failed");
2
        assert_eq!(tb.handler.state(), State::Busy);
2
        assert_eq!(tb.handler.step(), TransactionStep::Idle);
2
        assert!(tb.get_next_sent_pdu().is_none());
2
        let id = tb.handler.transaction_id().unwrap();
2
        tb.handler
2
            .cancel_request(&mut cfdp_user, &id)
2
            .expect("transaction cancellation failed");
2
        assert_eq!(tb.handler.state(), State::Idle);
2
        assert_eq!(tb.handler.step(), TransactionStep::Idle);
        // EOF (Cancel) PDU will be generated
2
        let eof_pdu = tb
2
            .get_next_sent_pdu()
2
            .expect("no EOF PDU generated like expected");
2
        assert_eq!(
2
            eof_pdu.file_directive_type.unwrap(),
2
            FileDirectiveType::EofPdu
2
        );
2
        let eof_pdu = EofPdu::from_bytes(&eof_pdu.raw_pdu).unwrap();
2
        assert_eq!(
2
            eof_pdu.condition_code(),
2
            ConditionCode::CancelRequestReceived
2
        );
2
        assert_eq!(eof_pdu.file_checksum(), 0);
2
        assert_eq!(eof_pdu.file_size(), 0);
2
        tb.common_pdu_check_for_file_transfer(eof_pdu.pdu_header(), CrcFlag::NoCrc);
2
    }
    #[test]
2
    fn test_cancelled_transfer_mid_transfer() {
2
        let fault_handler = TestFaultHandler::default();
2
        let test_sender = TestCfdpSender::default();
2
        let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 128);
2
        let mut file = OpenOptions::new()
2
            .write(true)
2
            .open(&tb.srcfile)
2
            .expect("opening file failed");
2
        let mut rand_data = [0u8; 140];
2
        rand::thread_rng().fill(&mut rand_data[..]);
2
        file.write_all(&rand_data)
2
            .expect("writing file content failed");
2
        drop(file);
2
        let put_request = PutRequestOwned::new_regular_request(
2
            REMOTE_ID.into(),
2
            &tb.srcfile,
2
            &tb.destfile,
2
            Some(TransmissionMode::Unacknowledged),
2
            Some(false),
2
        )
2
        .expect("creating put request failed");
2
        let file_size = rand_data.len() as u64;
2
        let mut cfdp_user = tb.create_user(0, file_size);
2
        let transaction_info =
2
            tb.common_no_acked_file_transfer(&mut cfdp_user, put_request, file_size);
2
        let mut chunks = rand_data.chunks(
2
            calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(
2
                &transaction_info.pdu_header,
2
                tb.max_packet_len,
2
                None,
2
            ),
2
        );
2
        let mut digest = CRC_32.digest();
2
        let first_chunk = chunks.next().expect("no chunk found");
2
        digest.update(first_chunk);
2
        let checksum = digest.finalize();
2
        let next_packet = tb.get_next_sent_pdu().unwrap();
2
        assert_eq!(next_packet.pdu_type, PduType::FileData);
2
        let fd_pdu = FileDataPdu::from_bytes(&next_packet.raw_pdu).unwrap();
2
        assert_eq!(fd_pdu.file_data(), &rand_data[0..first_chunk.len()]);
2
        let expected_id = tb.handler.transaction_id().unwrap();
2
        assert!(tb
2
            .handler
2
            .cancel_request(&mut cfdp_user, &expected_id)
2
            .expect("cancellation failed"));
2
        assert_eq!(tb.handler.state(), State::Idle);
2
        assert_eq!(tb.handler.step(), TransactionStep::Idle);
2
        let next_packet = tb.get_next_sent_pdu().unwrap();
2
        assert_eq!(next_packet.pdu_type, PduType::FileDirective);
2
        assert_eq!(
2
            next_packet.file_directive_type.unwrap(),
2
            FileDirectiveType::EofPdu
2
        );
        // As specified in 4.11.2.2 of the standard, the file size will be the progress of the
        // file copy operation so far, and the checksum is calculated for that progress.
2
        let eof_pdu = EofPdu::from_bytes(&next_packet.raw_pdu).expect("EOF PDU creation failed");
2
        assert_eq!(eof_pdu.file_size(), first_chunk.len() as u64);
2
        assert_eq!(eof_pdu.file_checksum(), checksum);
2
        assert_eq!(
2
            eof_pdu.condition_code(),
2
            ConditionCode::CancelRequestReceived
2
        );
2
    }
}