@@ -81,7 +81,6 @@ class CompletionDisposition(enum.Enum):
8181@dataclass
8282class _DestFileParams (_FileParamsBase ):
8383 file_name : Path
84- file_size_eof : int | None
8584
8685 @classmethod
8786 def empty (cls ) -> _DestFileParams :
@@ -91,38 +90,41 @@ def empty(cls) -> _DestFileParams:
9190 crc32 = b"" ,
9291 file_size = None ,
9392 file_name = Path (),
94- file_size_eof = None ,
93+ # file_size_eof=None,
9594 metadata_only = False ,
9695 )
9796
9897 def reset (self ) -> None :
9998 super ().reset ()
10099 self .file_name = Path ()
101- self .file_size_eof = None
102100
103101
104102class TransactionStep (enum .Enum ):
105103 IDLE = 0
106104 TRANSACTION_START = 1
107105 """Metadata was received, which triggered a transaction start."""
106+
108107 WAITING_FOR_METADATA = 2
109108 """Special state which is only used for acknowledged mode. The CFDP entity is still waiting
110109 for a missing metadata PDU to be re-sent. Until then, all arriving file data PDUs will only
111110 update the internal lost segment tracker. When the EOF PDU arrives, the state will be left.
112111 Please note that deferred lost segment handling might also be active when this state is set."""
112+
113113 RECEIVING_FILE_DATA = 3
114+
114115 RECV_FILE_DATA_WITH_CHECK_LIMIT_HANDLING = 4
115116 """This is the check timer step as specified in chapter 4.6.3.3 b) of the standard.
116117 The destination handler will still check for file data PDUs which might lead to a full
117118 file transfer completion."""
118- SENDING_EOF_ACK_PDU = 5
119- """Sending the ACK (EOF) packet."""
119+
120120 WAITING_FOR_MISSING_DATA = 6
121121 """Only relevant for acknowledged mode: Wait for lost metadata and file segments as part of
122122 the deferred lost segments detection procedure."""
123+
123124 TRANSFER_COMPLETION = 7
124125 """File transfer complete. Perform checksum verification and notice of completion. Please
125126 note that this does not necessarily mean that the file transfer was completed successfully."""
127+
126128 SENDING_FINISHED_PDU = 8
127129 WAITING_FOR_FINISHED_ACK = 9
128130
@@ -162,6 +164,7 @@ def coalesce_lost_segments(self) -> None:
162164 if len (self .lost_segments ) <= 1 :
163165 return
164166 merged_segments = []
167+ # Initialize to the first entry.
165168 current_start , current_end = next (iter (self .lost_segments .items ()))
166169
167170 for seg_start , seg_end in self .lost_segments .items ():
@@ -220,6 +223,8 @@ def remove_lost_segment(self, segment_to_remove: tuple[int, int]) -> bool:
220223@dataclass
221224class _AckedModeParams :
222225 lost_seg_tracker : LostSegmentTracker = field (default = LostSegmentTracker ())
226+ # Extra parameter: Missing metadata is not tracked inside the lost segment tracker, so we
227+ # need an extra parameter for this.
223228 metadata_missing : bool = False
224229 last_start_offset : int = 0
225230 last_end_offset : int = 0
@@ -520,11 +525,11 @@ def __idle_fsm(self, packet: GenericPduPacket | None) -> None:
520525 self ._start_transaction (metadata_pdu )
521526 else :
522527 raise ValueError (
523- f"unexpected configuration error: { pdu_holder .pdu } in " f" IDLE state machine"
528+ f"unexpected configuration error: { pdu_holder .pdu } in IDLE state machine"
524529 )
525530
526531 def __non_idle_fsm (self , packet : GenericPduPacket | None ) -> None :
527- self ._fsm_advancement_after_packets_were_sent ()
532+ self ._assert_all_packets_were_sent ()
528533 pdu_holder = PduHolder (packet )
529534 if (
530535 self .states .step
@@ -554,20 +559,10 @@ def __non_idle_fsm(self, packet: GenericPduPacket | None) -> None:
554559 if self .states .step == TransactionStep .WAITING_FOR_FINISHED_ACK :
555560 self ._handle_waiting_for_finished_ack (pdu_holder )
556561
557- def _fsm_advancement_after_packets_were_sent (self ) -> None :
562+ def _assert_all_packets_were_sent (self ) -> None :
558563 """Advance the internal FSM after all packets to be sent were retrieved from the handler."""
559564 if len (self ._pdus_to_be_sent ) > 0 :
560565 raise UnretrievedPdusToBeSent (f"{ len (self ._pdus_to_be_sent )} packets left to send" )
561- if self .states .step == TransactionStep .SENDING_EOF_ACK_PDU :
562- if (
563- self ._params .acked_params .lost_seg_tracker .num_lost_segments > 0
564- or self ._params .acked_params .metadata_missing
565- ):
566- self ._start_deferred_lost_segment_handling ()
567- else :
568- if self ._params .completion_disposition != CompletionDisposition .CANCELED :
569- self ._checksum_verify ()
570- self .states .step = TransactionStep .TRANSFER_COMPLETION
571566
572567 def _start_transaction (self , metadata_pdu : MetadataPdu ) -> bool :
573568 if self .states .state != CfdpState .IDLE :
@@ -595,7 +590,7 @@ def _start_transaction_missing_metadata_recv_eof(self, eof_pdu: EofPdu) -> None:
595590
596591 def _handle_eof_without_previous_metadata (self , eof_pdu : EofPdu ) -> None :
597592 self ._params .fp .progress = eof_pdu .file_size
598- self ._params .fp .file_size_eof = eof_pdu .file_size
593+ self ._params .fp .file_size = eof_pdu .file_size
599594 self ._params .acked_params .metadata_missing = True
600595 if self ._params .fp .progress > 0 :
601596 # Clear old list, deferred procedure for the whole file is now active.
@@ -609,11 +604,23 @@ def _handle_eof_without_previous_metadata(self, eof_pdu: EofPdu) -> None:
609604 assert self ._params .transaction_id is not None
610605 self .user .eof_recv_indication (self ._params .transaction_id )
611606 self ._prepare_eof_ack_packet ()
612- self .states .step = TransactionStep .SENDING_EOF_ACK_PDU
607+ self ._eof_ack_pdu_done ()
608+
609+ def _eof_ack_pdu_done (self ) -> None :
610+ if (
611+ self ._params .acked_params .lost_seg_tracker .num_lost_segments > 0
612+ or self ._params .acked_params .metadata_missing
613+ ):
614+ self ._start_deferred_lost_segment_handling ()
615+
616+ else :
617+ if self ._params .completion_disposition != CompletionDisposition .CANCELED :
618+ self ._checksum_verify ()
619+ self .states .step = TransactionStep .TRANSFER_COMPLETION
613620
614621 def _start_transaction_missing_metadata_recv_fd (self , fd_pdu : FileDataPdu ) -> None :
615622 self ._common_first_packet_not_metadata_pdu_handler (fd_pdu )
616- self ._handle_fd_without_previous_metadata (True , fd_pdu )
623+ self ._handle_fd_without_previous_metadata (fd_pdu )
617624
618625 def _handle_finished_pdu_sent (self ) -> None :
619626 if (
@@ -625,19 +632,16 @@ def _handle_finished_pdu_sent(self) -> None:
625632 return
626633 self ._reset_internal (False )
627634
628- def _handle_fd_without_previous_metadata (self , first_pdu : bool , fd_pdu : FileDataPdu ) -> None :
635+ def _handle_fd_without_previous_metadata (self , fd_pdu : FileDataPdu ) -> None :
629636 self ._params .fp .progress = fd_pdu .offset + len (fd_pdu .file_data )
630637 if len (fd_pdu .file_data ) > 0 :
631- start = fd_pdu .offset
632- if first_pdu :
633- start = 0
634638 # I will just wait until the metadata has been received with re-requesting the file
635639 # data PDU. How does the standard expect me to process file data PDUs where I do not
636640 # even know the filenames? How would I even generically do this?
637641 # I will add this file segment (and all others which came before and might be missing
638642 # as well) to the lost segment list.
639643 self ._params .acked_params .lost_seg_tracker .add_lost_segment (
640- (start , self ._params .fp .progress )
644+ (0 , self ._params .fp .progress )
641645 )
642646 # This is a bit tricky: We need to set those variables to an appropriate value so
643647 # the removal of handled lost segments works properly. However, we can not set the
@@ -649,8 +653,7 @@ def _handle_fd_without_previous_metadata(self, first_pdu: bool, fd_pdu: FileData
649653 # Re-request the metadata PDU.
650654 if self ._params .remote_cfg .immediate_nak_mode :
651655 lost_segments : list [tuple [int , int ]] = []
652- if first_pdu :
653- lost_segments .append ((0 , 0 ))
656+ lost_segments .append ((0 , 0 ))
654657 if len (fd_pdu .file_data ) > 0 :
655658 lost_segments .append ((0 , self ._params .fp .progress ))
656659 if len (lost_segments ) > 0 :
@@ -698,7 +701,7 @@ def _handle_metadata_packet(self, metadata_pdu: MetadataPdu) -> None:
698701 # sender.
699702 if self ._params .remote_cfg is None :
700703 _LOGGER .warning (
701- "No remote configuration found for remote ID" f" { metadata_pdu .dest_entity_id } "
704+ f "No remote configuration found for remote ID { metadata_pdu .dest_entity_id } "
702705 )
703706 raise NoRemoteEntityCfgFound (metadata_pdu .dest_entity_id )
704707 if not self ._params .fp .metadata_only :
@@ -753,7 +756,7 @@ def _handle_waiting_for_missing_metadata(self, packet_holder: PduHolder) -> None
753756 if packet_holder .pdu is None :
754757 return
755758 if packet_holder .pdu_type == PduType .FILE_DATA :
756- self ._handle_fd_without_previous_metadata (True , packet_holder .to_file_data_pdu ())
759+ self ._handle_fd_without_previous_metadata (packet_holder .to_file_data_pdu ())
757760 elif packet_holder .pdu_directive_type == DirectiveType .METADATA_PDU :
758761 self ._handle_metadata_packet (packet_holder .to_metadata_pdu ())
759762 if self ._params .acked_params .deferred_lost_segment_detection_active :
@@ -832,8 +835,8 @@ def _handle_fd_pdu(self, file_data_pdu: FileDataPdu) -> None:
832835 self ._params .finished_params .file_status = FileStatus .FILE_RETAINED
833836
834837 if (
835- self ._params .fp .file_size_eof is not None
836- and (offset + len (file_data_pdu .file_data ) > self ._params .fp .file_size_eof )
838+ self ._params .fp .file_size is not None
839+ and (offset + len (file_data_pdu .file_data ) > self ._params .fp .file_size )
837840 and (
838841 self ._declare_fault (ConditionCode .FILE_SIZE_ERROR )
839842 != FaultHandlerCode .IGNORE_ERROR
@@ -895,7 +898,7 @@ def _deferred_lost_segment_handling(self) -> None:
895898 if not self ._params .acked_params .deferred_lost_segment_detection_active :
896899 return
897900 assert self ._params .remote_cfg is not None
898- assert self ._params .fp .file_size_eof is not None
901+ assert self ._params .fp .file_size is not None
899902 if (
900903 self ._params .acked_params .lost_seg_tracker .num_lost_segments == 0
901904 and not self ._params .acked_params .metadata_missing
@@ -941,7 +944,7 @@ def _deferred_lost_segment_handling(self) -> None:
941944 NakPdu (
942945 self ._params .pdu_conf ,
943946 0 ,
944- self ._params .fp .file_size_eof ,
947+ self ._params .fp .file_size ,
945948 next_segment_reqs ,
946949 )
947950 )
@@ -951,7 +954,7 @@ def _deferred_lost_segment_handling(self) -> None:
951954 NakPdu (
952955 self ._params .pdu_conf ,
953956 0 ,
954- self ._params .fp .file_size_eof ,
957+ self ._params .fp .file_size ,
955958 next_segment_reqs ,
956959 )
957960 )
@@ -962,7 +965,7 @@ def _deferred_lost_segment_handling(self) -> None:
962965 def _handle_eof_pdu (self , eof_pdu : EofPdu ) -> bool | None :
963966 """Returns whether to exit the FSM prematurely."""
964967 self ._params .fp .crc32 = eof_pdu .file_checksum
965- self ._params .fp .file_size_eof = eof_pdu .file_size
968+ self ._params .fp .file_size = eof_pdu .file_size
966969 if self .cfg .indication_cfg .eof_recv_indication_required :
967970 assert self ._params .transaction_id is not None
968971 self .user .eof_recv_indication (self ._params .transaction_id )
@@ -978,30 +981,26 @@ def _handle_eof_pdu(self, eof_pdu: EofPdu) -> bool | None:
978981 EntityIdTlv (self ._params .remote_cfg .entity_id .as_bytes ),
979982 )
980983 # Store this as progress for the checksum calculation.
981- self ._params .fp .progress = self ._params .fp .file_size_eof
984+ self ._params .fp .progress = self ._params .fp .file_size
982985 self ._params .finished_params .delivery_code = DeliveryCode .DATA_INCOMPLETE
983986 self ._file_transfer_complete_transition ()
984987 return False
985988
986989 def _handle_no_error_eof (self ) -> bool :
987990 """Returns whether the transfer can be completed regularly."""
988991 # CFDP 4.6.1.2.9: Declare file size error if progress exceeds file size
989- if self ._params .fp .progress > self ._params .fp .file_size_eof : # type: ignore
992+ if self ._params .fp .progress > self ._params .fp .file_size : # type: ignore
990993 if self ._declare_fault (ConditionCode .FILE_SIZE_ERROR ) != FaultHandlerCode .IGNORE_ERROR :
991994 return False
992995 elif (
993- self ._params .fp .progress < self ._params .fp .file_size_eof # type: ignore
996+ self ._params .fp .progress < self ._params .fp .file_size # type: ignore
994997 ) and self .transmission_mode == TransmissionMode .ACKNOWLEDGED :
995998 # CFDP 4.6.4.3.1: The end offset of the last received file segment and the file
996999 # size as stated in the EOF PDU is not the same, so we need to add that segment to
9971000 # the lost segments for the deferred lost segment detection procedure.
9981001 self ._params .acked_params .lost_seg_tracker .add_lost_segment (
999- (self ._params .fp .progress , self ._params .fp .file_size_eof ) # type: ignore
1002+ (self ._params .fp .progress , self ._params .fp .file_size ) # type: ignore
10001003 )
1001- if self ._params .fp .file_size_eof != self ._params .fp .file_size :
1002- # Can or should this ever happen for a No Error EOF? Treat this like a non-fatal
1003- # error for now.
1004- _LOGGER .warning ("missmatch of EOF file size and Metadata File Size for success EOF" )
10051004 if (
10061005 self .transmission_mode == TransmissionMode .UNACKNOWLEDGED
10071006 and not self ._checksum_verify ()
@@ -1022,8 +1021,8 @@ def _start_deferred_lost_segment_handling(self) -> None:
10221021 self .states .step = TransactionStep .WAITING_FOR_MISSING_DATA
10231022 self ._params .acked_params .deferred_lost_segment_detection_active = True
10241023 self ._params .acked_params .lost_seg_tracker .coalesce_lost_segments ()
1025- self ._params .acked_params .last_start_offset = self ._params .fp .file_size_eof # type: ignore
1026- self ._params .acked_params .last_end_offset = self ._params .fp .file_size_eof # type: ignore
1024+ self ._params .acked_params .last_start_offset = self ._params .fp .file_size # type: ignore
1025+ self ._params .acked_params .last_end_offset = self ._params .fp .file_size # type: ignore
10271026 self ._deferred_lost_segment_handling ()
10281027
10291028 def _prepare_eof_ack_packet (self ) -> None :
@@ -1062,7 +1061,7 @@ def _file_transfer_complete_transition(self) -> None:
10621061 self .states .step = TransactionStep .TRANSFER_COMPLETION
10631062 elif self .transmission_mode == TransmissionMode .ACKNOWLEDGED :
10641063 self ._prepare_eof_ack_packet ()
1065- self .states . step = TransactionStep . SENDING_EOF_ACK_PDU
1064+ self ._eof_ack_pdu_done ()
10661065
10671066 def _trigger_notice_of_completion_canceled (
10681067 self , condition_code : ConditionCode , fault_location : EntityIdTlv
@@ -1102,8 +1101,6 @@ def _notice_of_completion(self) -> None:
11021101 self .user .transaction_finished_indication (finished_indic_params )
11031102
11041103 def _prepare_finished_pdu (self ) -> None :
1105- if self .states .packets_ready :
1106- raise UnretrievedPdusToBeSent
11071104 # TODO: Fault location handling. Set remote entity ID for file copy
11081105 # operations cancelled with an EOF (Cancel) PDU, and the local ID for file
11091106 # copy operations cancelled with the local API.
0 commit comments