pycfdp 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cfdp/__init__.py +106 -0
- cfdp/checksum.py +94 -0
- cfdp/constants.py +153 -0
- cfdp/core.py +895 -0
- cfdp/event.py +55 -0
- cfdp/filestore/__init__.py +2 -0
- cfdp/filestore/base.py +99 -0
- cfdp/filestore/native.py +162 -0
- cfdp/meta/__init__.py +3 -0
- cfdp/meta/datafield.py +11 -0
- cfdp/meta/filestore.py +127 -0
- cfdp/meta/message.py +659 -0
- cfdp/pdu/__init__.py +8 -0
- cfdp/pdu/ack.py +90 -0
- cfdp/pdu/eof.py +123 -0
- cfdp/pdu/filedata.py +90 -0
- cfdp/pdu/finished.py +99 -0
- cfdp/pdu/header.py +159 -0
- cfdp/pdu/keep_alive.py +108 -0
- cfdp/pdu/metadata.py +211 -0
- cfdp/pdu/nak.py +220 -0
- cfdp/pdu/prompt.py +75 -0
- cfdp/pure/__init__.py +68 -0
- cfdp/pure/config.py +141 -0
- cfdp/pure/indications.py +130 -0
- cfdp/pure/machines/__init__.py +25 -0
- cfdp/pure/machines/receiver1.py +433 -0
- cfdp/pure/machines/receiver2.py +893 -0
- cfdp/pure/machines/sender1.py +400 -0
- cfdp/pure/machines/sender2.py +774 -0
- cfdp/pure/outputs.py +203 -0
- cfdp/pure/query_port.py +48 -0
- cfdp/pure/transaction.py +43 -0
- cfdp/py.typed +0 -0
- cfdp/remote_entity_config.py +12 -0
- cfdp/shell/__init__.py +44 -0
- cfdp/shell/checksum_service.py +136 -0
- cfdp/shell/dispatcher.py +185 -0
- cfdp/shell/effect_executor.py +285 -0
- cfdp/shell/machine_registry.py +165 -0
- cfdp/shell/query_port.py +70 -0
- cfdp/shell/timer_service.py +252 -0
- cfdp/transaction_handle.py +50 -0
- cfdp/transport/__init__.py +0 -0
- cfdp/transport/base.py +12 -0
- cfdp/transport/spacepacket.py +47 -0
- cfdp/transport/udp.py +90 -0
- cfdp/transport/zmq.py +54 -0
- pycfdp-0.2.2.dist-info/METADATA +76 -0
- pycfdp-0.2.2.dist-info/RECORD +53 -0
- pycfdp-0.2.2.dist-info/WHEEL +5 -0
- pycfdp-0.2.2.dist-info/licenses/LICENSE.txt +19 -0
- pycfdp-0.2.2.dist-info/top_level.txt +1 -0
cfdp/pdu/ack.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from cfdp.checksum import finalize_pdu, strip_pdu_crc
|
|
2
|
+
from cfdp.constants import DirectiveCode, PduTypeCode
|
|
3
|
+
|
|
4
|
+
from .header import PduHeader
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AckPdu:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
# pdu header parameters
|
|
11
|
+
direction=None,
|
|
12
|
+
transmission_mode=None,
|
|
13
|
+
crc_flag=0,
|
|
14
|
+
large_file_flag=0,
|
|
15
|
+
pdu_data_field_length=None,
|
|
16
|
+
segmentation_control=0,
|
|
17
|
+
length_of_entity_ids=0,
|
|
18
|
+
segmentation_metadata_flag=0,
|
|
19
|
+
length_of_seq_number=0,
|
|
20
|
+
source_entity_id=None,
|
|
21
|
+
transaction_seq_number=None,
|
|
22
|
+
destination_entity_id=None,
|
|
23
|
+
# ack pdu parameters
|
|
24
|
+
directive_code_ack=None,
|
|
25
|
+
directive_subtype_code=None,
|
|
26
|
+
condition_code=None,
|
|
27
|
+
transaction_status=None,
|
|
28
|
+
):
|
|
29
|
+
self.directive_code = DirectiveCode.ACK
|
|
30
|
+
self.directive_code_ack = directive_code_ack
|
|
31
|
+
self.directive_subtype_code = directive_subtype_code
|
|
32
|
+
self.condition_code = condition_code
|
|
33
|
+
self.transaction_status = transaction_status
|
|
34
|
+
|
|
35
|
+
self.pdu_header = PduHeader(
|
|
36
|
+
pdu_type=PduTypeCode.FILE_DIRECTIVE,
|
|
37
|
+
direction=direction,
|
|
38
|
+
transmission_mode=transmission_mode,
|
|
39
|
+
crc_flag=crc_flag,
|
|
40
|
+
large_file_flag=large_file_flag,
|
|
41
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
42
|
+
segmentation_control=segmentation_control,
|
|
43
|
+
length_of_entity_ids=length_of_entity_ids,
|
|
44
|
+
segmentation_metadata_flag=segmentation_metadata_flag,
|
|
45
|
+
length_of_seq_number=length_of_seq_number,
|
|
46
|
+
source_entity_id=source_entity_id,
|
|
47
|
+
transaction_seq_number=transaction_seq_number,
|
|
48
|
+
destination_entity_id=destination_entity_id,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def encode(self):
|
|
52
|
+
databytes = bytes(
|
|
53
|
+
[
|
|
54
|
+
self.directive_code,
|
|
55
|
+
(self.directive_code_ack << 4) + (self.directive_subtype_code),
|
|
56
|
+
(self.condition_code << 4) + (self.transaction_status),
|
|
57
|
+
]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return finalize_pdu(self.pdu_header, databytes)
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def decode(cls, pdu):
|
|
64
|
+
"""ref: CCSDS 720.2-G-3 Page 2-10"""
|
|
65
|
+
|
|
66
|
+
pdu = strip_pdu_crc(pdu)
|
|
67
|
+
pdu_header = PduHeader.decode(pdu)
|
|
68
|
+
pdu_data = pdu[len(pdu_header) :]
|
|
69
|
+
pdu_data_field_length = len(pdu_data)
|
|
70
|
+
|
|
71
|
+
return cls(
|
|
72
|
+
# pdu header parameters
|
|
73
|
+
direction=pdu_header.direction,
|
|
74
|
+
transmission_mode=pdu_header.transmission_mode,
|
|
75
|
+
crc_flag=pdu_header.crc_flag,
|
|
76
|
+
large_file_flag=pdu_header.large_file_flag,
|
|
77
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
78
|
+
segmentation_control=pdu_header.segmentation_control,
|
|
79
|
+
length_of_entity_ids=pdu_header.length_of_entity_ids,
|
|
80
|
+
segmentation_metadata_flag=pdu_header.segmentation_metadata_flag,
|
|
81
|
+
length_of_seq_number=pdu_header.length_of_seq_number,
|
|
82
|
+
source_entity_id=pdu_header.source_entity_id,
|
|
83
|
+
transaction_seq_number=pdu_header.transaction_seq_number,
|
|
84
|
+
destination_entity_id=pdu_header.destination_entity_id,
|
|
85
|
+
# ack pdu parameters
|
|
86
|
+
directive_code_ack=(pdu_data[1] >> 4),
|
|
87
|
+
directive_subtype_code=(pdu_data[1] & 0x0F),
|
|
88
|
+
condition_code=(pdu_data[2] >> 4),
|
|
89
|
+
transaction_status=(pdu_data[2] & 0x03),
|
|
90
|
+
)
|
cfdp/pdu/eof.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from cfdp.checksum import finalize_pdu, strip_pdu_crc
|
|
2
|
+
from cfdp.constants import ConditionCode, DirectiveCode, PduTypeCode, TypeFieldCode
|
|
3
|
+
|
|
4
|
+
from .header import PduHeader
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EofPdu:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
# pdu header parameters
|
|
11
|
+
direction=None,
|
|
12
|
+
transmission_mode=None,
|
|
13
|
+
crc_flag=0,
|
|
14
|
+
large_file_flag=0,
|
|
15
|
+
pdu_data_field_length=None,
|
|
16
|
+
segmentation_control=0,
|
|
17
|
+
length_of_entity_ids=0,
|
|
18
|
+
segmentation_metadata_flag=0,
|
|
19
|
+
length_of_seq_number=0,
|
|
20
|
+
source_entity_id=None,
|
|
21
|
+
transaction_seq_number=None,
|
|
22
|
+
destination_entity_id=None,
|
|
23
|
+
# eof pdu parameters
|
|
24
|
+
condition_code=None,
|
|
25
|
+
file_checksum=None,
|
|
26
|
+
file_size=None,
|
|
27
|
+
fault_location=None,
|
|
28
|
+
):
|
|
29
|
+
self.directive_code = DirectiveCode.EOF
|
|
30
|
+
self.condition_code = condition_code
|
|
31
|
+
self.file_checksum = file_checksum
|
|
32
|
+
self.file_size = file_size
|
|
33
|
+
self.fault_location = fault_location
|
|
34
|
+
|
|
35
|
+
if file_size is not None and file_size > 0xFFFFFFFF:
|
|
36
|
+
large_file_flag = 1
|
|
37
|
+
|
|
38
|
+
self.pdu_header = PduHeader(
|
|
39
|
+
pdu_type=PduTypeCode.FILE_DIRECTIVE,
|
|
40
|
+
direction=direction,
|
|
41
|
+
transmission_mode=transmission_mode,
|
|
42
|
+
crc_flag=crc_flag,
|
|
43
|
+
large_file_flag=large_file_flag,
|
|
44
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
45
|
+
segmentation_control=segmentation_control,
|
|
46
|
+
length_of_entity_ids=length_of_entity_ids,
|
|
47
|
+
segmentation_metadata_flag=segmentation_metadata_flag,
|
|
48
|
+
length_of_seq_number=length_of_seq_number,
|
|
49
|
+
source_entity_id=source_entity_id,
|
|
50
|
+
transaction_seq_number=transaction_seq_number,
|
|
51
|
+
destination_entity_id=destination_entity_id,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def encode(self):
|
|
55
|
+
# Reference: CCSDS 727.0-B-5, 5.2.2, Table 5-6: End-of-File PDU Contents
|
|
56
|
+
|
|
57
|
+
databytes = bytes(
|
|
58
|
+
[
|
|
59
|
+
self.directive_code,
|
|
60
|
+
self.condition_code << 4,
|
|
61
|
+
]
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
databytes += int(self.file_checksum).to_bytes(4, "big")
|
|
65
|
+
|
|
66
|
+
file_size_length = 8 if self.pdu_header.large_file_flag else 4
|
|
67
|
+
databytes += int(self.file_size).to_bytes(file_size_length, "big")
|
|
68
|
+
|
|
69
|
+
if self.condition_code != ConditionCode.NO_ERROR:
|
|
70
|
+
id_len = self.pdu_header.length_of_entity_ids + 1
|
|
71
|
+
databytes += bytes(
|
|
72
|
+
[
|
|
73
|
+
TypeFieldCode.ENTITY_ID,
|
|
74
|
+
id_len,
|
|
75
|
+
]
|
|
76
|
+
)
|
|
77
|
+
# Not sure: is it correct to send our own entity ID here?
|
|
78
|
+
databytes += int(self.pdu_header.source_entity_id).to_bytes(id_len, "big")
|
|
79
|
+
|
|
80
|
+
return finalize_pdu(self.pdu_header, databytes)
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def decode(cls, pdu):
|
|
84
|
+
pdu = strip_pdu_crc(pdu)
|
|
85
|
+
pdu_header = PduHeader.decode(pdu)
|
|
86
|
+
pdu_data = pdu[len(pdu_header) :]
|
|
87
|
+
pdu_data_field_length = len(pdu_data)
|
|
88
|
+
|
|
89
|
+
file_size_length = 8 if pdu_header.large_file_flag else 4
|
|
90
|
+
file_size = int.from_bytes(pdu_data[6 : 6 + file_size_length], "big")
|
|
91
|
+
|
|
92
|
+
# Parse optional fault_location TLV that follows the file_size field
|
|
93
|
+
# when condition_code != NO_ERROR (TypeFieldCode.ENTITY_ID = 0x06)
|
|
94
|
+
fault_location = None
|
|
95
|
+
tlv_offset = 6 + file_size_length
|
|
96
|
+
if tlv_offset + 2 <= len(pdu_data):
|
|
97
|
+
if pdu_data[tlv_offset] == TypeFieldCode.ENTITY_ID:
|
|
98
|
+
id_len = pdu_data[tlv_offset + 1]
|
|
99
|
+
if tlv_offset + 2 + id_len <= len(pdu_data):
|
|
100
|
+
fault_location = int.from_bytes(
|
|
101
|
+
pdu_data[tlv_offset + 2 : tlv_offset + 2 + id_len], "big"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return cls(
|
|
105
|
+
# pdu header parameters
|
|
106
|
+
direction=pdu_header.direction,
|
|
107
|
+
transmission_mode=pdu_header.transmission_mode,
|
|
108
|
+
crc_flag=pdu_header.crc_flag,
|
|
109
|
+
large_file_flag=pdu_header.large_file_flag,
|
|
110
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
111
|
+
segmentation_control=pdu_header.segmentation_control,
|
|
112
|
+
length_of_entity_ids=pdu_header.length_of_entity_ids,
|
|
113
|
+
segmentation_metadata_flag=pdu_header.segmentation_metadata_flag,
|
|
114
|
+
length_of_seq_number=pdu_header.length_of_seq_number,
|
|
115
|
+
source_entity_id=pdu_header.source_entity_id,
|
|
116
|
+
transaction_seq_number=pdu_header.transaction_seq_number,
|
|
117
|
+
destination_entity_id=pdu_header.destination_entity_id,
|
|
118
|
+
# eof pdu parameters
|
|
119
|
+
condition_code=pdu_data[1] >> 4,
|
|
120
|
+
file_checksum=int.from_bytes(pdu_data[2:6], "big"),
|
|
121
|
+
file_size=file_size,
|
|
122
|
+
fault_location=fault_location,
|
|
123
|
+
)
|
cfdp/pdu/filedata.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from cfdp.checksum import finalize_pdu, strip_pdu_crc
|
|
2
|
+
from cfdp.constants import PduTypeCode
|
|
3
|
+
|
|
4
|
+
from .header import PduHeader
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FiledataPdu:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
# pdu header parameters
|
|
11
|
+
direction=None,
|
|
12
|
+
transmission_mode=None,
|
|
13
|
+
crc_flag=0,
|
|
14
|
+
large_file_flag=0,
|
|
15
|
+
pdu_data_field_length=None,
|
|
16
|
+
segmentation_control=0,
|
|
17
|
+
length_of_entity_ids=0,
|
|
18
|
+
segmentation_metadata_flag=0,
|
|
19
|
+
length_of_seq_number=0,
|
|
20
|
+
source_entity_id=None,
|
|
21
|
+
transaction_seq_number=None,
|
|
22
|
+
destination_entity_id=None,
|
|
23
|
+
# filedata pdu parameters
|
|
24
|
+
record_continuation_state=None,
|
|
25
|
+
segment_metadata_length=None,
|
|
26
|
+
segment_metadata=None,
|
|
27
|
+
segment_offset=None,
|
|
28
|
+
file_data=None,
|
|
29
|
+
):
|
|
30
|
+
if None in [segment_offset, file_data]:
|
|
31
|
+
raise Exception("Must provide all required parameters")
|
|
32
|
+
|
|
33
|
+
self.segment_offset = segment_offset
|
|
34
|
+
self.file_data = file_data
|
|
35
|
+
|
|
36
|
+
self.pdu_header = PduHeader(
|
|
37
|
+
pdu_type=PduTypeCode.FILE_DATA,
|
|
38
|
+
direction=direction,
|
|
39
|
+
transmission_mode=transmission_mode,
|
|
40
|
+
crc_flag=crc_flag,
|
|
41
|
+
large_file_flag=large_file_flag,
|
|
42
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
43
|
+
segmentation_control=segmentation_control,
|
|
44
|
+
length_of_entity_ids=length_of_entity_ids,
|
|
45
|
+
segmentation_metadata_flag=segmentation_metadata_flag,
|
|
46
|
+
length_of_seq_number=length_of_seq_number,
|
|
47
|
+
source_entity_id=source_entity_id,
|
|
48
|
+
transaction_seq_number=transaction_seq_number,
|
|
49
|
+
destination_entity_id=destination_entity_id,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def encode(self):
|
|
53
|
+
if self.pdu_header.segmentation_metadata_flag:
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
databytes = bytes(
|
|
56
|
+
[
|
|
57
|
+
(self.segment_offset >> 24) & 0xFF,
|
|
58
|
+
(self.segment_offset >> 16) & 0xFF,
|
|
59
|
+
(self.segment_offset >> 8) & 0xFF,
|
|
60
|
+
(self.segment_offset) & 0xFF,
|
|
61
|
+
]
|
|
62
|
+
)
|
|
63
|
+
databytes += self.file_data
|
|
64
|
+
return finalize_pdu(self.pdu_header, databytes)
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def decode(cls, pdu):
|
|
68
|
+
pdu = strip_pdu_crc(pdu)
|
|
69
|
+
pdu_header = PduHeader.decode(pdu)
|
|
70
|
+
pdu_data = pdu[len(pdu_header) :]
|
|
71
|
+
pdu_data_field_length = len(pdu_data)
|
|
72
|
+
|
|
73
|
+
return cls(
|
|
74
|
+
# pdu header parameters
|
|
75
|
+
direction=pdu_header.direction,
|
|
76
|
+
transmission_mode=pdu_header.transmission_mode,
|
|
77
|
+
crc_flag=pdu_header.crc_flag,
|
|
78
|
+
large_file_flag=pdu_header.large_file_flag,
|
|
79
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
80
|
+
segmentation_control=pdu_header.segmentation_control,
|
|
81
|
+
length_of_entity_ids=pdu_header.length_of_entity_ids,
|
|
82
|
+
segmentation_metadata_flag=pdu_header.segmentation_metadata_flag,
|
|
83
|
+
length_of_seq_number=pdu_header.length_of_seq_number,
|
|
84
|
+
source_entity_id=pdu_header.source_entity_id,
|
|
85
|
+
transaction_seq_number=pdu_header.transaction_seq_number,
|
|
86
|
+
destination_entity_id=pdu_header.destination_entity_id,
|
|
87
|
+
# filedata pdu parameters
|
|
88
|
+
segment_offset=int.from_bytes(pdu_data[0:4], "big"),
|
|
89
|
+
file_data=pdu_data[4:],
|
|
90
|
+
)
|
cfdp/pdu/finished.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from cfdp.checksum import finalize_pdu, strip_pdu_crc
|
|
2
|
+
from cfdp.constants import DirectiveCode, PduTypeCode
|
|
3
|
+
|
|
4
|
+
from .header import PduHeader
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FinishedPdu:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
# pdu header parameters
|
|
11
|
+
direction=None,
|
|
12
|
+
transmission_mode=None,
|
|
13
|
+
crc_flag=0,
|
|
14
|
+
large_file_flag=0,
|
|
15
|
+
pdu_data_field_length=None,
|
|
16
|
+
segmentation_control=0,
|
|
17
|
+
length_of_entity_ids=0,
|
|
18
|
+
segmentation_metadata_flag=0,
|
|
19
|
+
length_of_seq_number=0,
|
|
20
|
+
source_entity_id=None,
|
|
21
|
+
transaction_seq_number=None,
|
|
22
|
+
destination_entity_id=None,
|
|
23
|
+
# finished pdu parameters
|
|
24
|
+
condition_code=None,
|
|
25
|
+
delivery_code=None,
|
|
26
|
+
file_status=None,
|
|
27
|
+
filestore_responses=None,
|
|
28
|
+
fault_location=None,
|
|
29
|
+
):
|
|
30
|
+
self.directive_code = DirectiveCode.FINISHED
|
|
31
|
+
self.condition_code = condition_code
|
|
32
|
+
self.delivery_code = delivery_code
|
|
33
|
+
self.file_status = file_status
|
|
34
|
+
self.filestore_responses = filestore_responses
|
|
35
|
+
|
|
36
|
+
self.pdu_header = PduHeader(
|
|
37
|
+
pdu_type=PduTypeCode.FILE_DIRECTIVE,
|
|
38
|
+
direction=direction,
|
|
39
|
+
transmission_mode=transmission_mode,
|
|
40
|
+
crc_flag=crc_flag,
|
|
41
|
+
large_file_flag=large_file_flag,
|
|
42
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
43
|
+
segmentation_control=segmentation_control,
|
|
44
|
+
length_of_entity_ids=length_of_entity_ids,
|
|
45
|
+
segmentation_metadata_flag=segmentation_metadata_flag,
|
|
46
|
+
length_of_seq_number=length_of_seq_number,
|
|
47
|
+
source_entity_id=source_entity_id,
|
|
48
|
+
transaction_seq_number=transaction_seq_number,
|
|
49
|
+
destination_entity_id=destination_entity_id,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def encode(self):
|
|
53
|
+
databytes = bytes(
|
|
54
|
+
[
|
|
55
|
+
(self.directive_code),
|
|
56
|
+
(self.condition_code << 4)
|
|
57
|
+
+ (1 << 3)
|
|
58
|
+
+ (self.delivery_code << 2) # end system status must be 1
|
|
59
|
+
+ (self.file_status),
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if self.filestore_responses:
|
|
64
|
+
for filestore_responses in self.filestore_responses:
|
|
65
|
+
databytes += filestore_responses.encode()
|
|
66
|
+
raise NotImplementedError
|
|
67
|
+
|
|
68
|
+
return finalize_pdu(self.pdu_header, databytes)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def decode(cls, pdu):
|
|
72
|
+
"""ref: CCSDS 720.2-G-3 Page 2-9"""
|
|
73
|
+
|
|
74
|
+
pdu = strip_pdu_crc(pdu)
|
|
75
|
+
pdu_header = PduHeader.decode(pdu)
|
|
76
|
+
pdu_data = pdu[len(pdu_header) :]
|
|
77
|
+
pdu_data_field_length = len(pdu_data)
|
|
78
|
+
|
|
79
|
+
return cls(
|
|
80
|
+
# pdu header parameters
|
|
81
|
+
direction=pdu_header.direction,
|
|
82
|
+
transmission_mode=pdu_header.transmission_mode,
|
|
83
|
+
crc_flag=pdu_header.crc_flag,
|
|
84
|
+
large_file_flag=pdu_header.large_file_flag,
|
|
85
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
86
|
+
segmentation_control=pdu_header.segmentation_control,
|
|
87
|
+
length_of_entity_ids=pdu_header.length_of_entity_ids,
|
|
88
|
+
segmentation_metadata_flag=pdu_header.segmentation_metadata_flag,
|
|
89
|
+
length_of_seq_number=pdu_header.length_of_seq_number,
|
|
90
|
+
source_entity_id=pdu_header.source_entity_id,
|
|
91
|
+
transaction_seq_number=pdu_header.transaction_seq_number,
|
|
92
|
+
destination_entity_id=pdu_header.destination_entity_id,
|
|
93
|
+
# finished pdu parameters
|
|
94
|
+
condition_code=pdu_data[1] >> 4,
|
|
95
|
+
delivery_code=(pdu_data[1] >> 2) & 0x01,
|
|
96
|
+
file_status=pdu_data[1] & 0x03,
|
|
97
|
+
filestore_responses=0, # TODO
|
|
98
|
+
fault_location=None,
|
|
99
|
+
)
|
cfdp/pdu/header.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from cfdp.meta import get_required_octets
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from cfdp.pure.transaction import TransactionId
|
|
7
|
+
|
|
8
|
+
CFDP_PROTOCOL_VERSION = 1
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PduHeader:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
pdu_type=None,
|
|
15
|
+
direction=None,
|
|
16
|
+
transmission_mode=None,
|
|
17
|
+
crc_flag=0,
|
|
18
|
+
large_file_flag=0,
|
|
19
|
+
pdu_data_field_length=None,
|
|
20
|
+
segmentation_control=0,
|
|
21
|
+
length_of_entity_ids=0,
|
|
22
|
+
segmentation_metadata_flag=0,
|
|
23
|
+
length_of_seq_number=0,
|
|
24
|
+
source_entity_id=None,
|
|
25
|
+
transaction_seq_number=None,
|
|
26
|
+
destination_entity_id=None,
|
|
27
|
+
):
|
|
28
|
+
if None in [
|
|
29
|
+
pdu_type,
|
|
30
|
+
direction,
|
|
31
|
+
transmission_mode,
|
|
32
|
+
source_entity_id,
|
|
33
|
+
transaction_seq_number,
|
|
34
|
+
destination_entity_id,
|
|
35
|
+
]:
|
|
36
|
+
raise Exception("Must provide all required parameters")
|
|
37
|
+
|
|
38
|
+
self.version = CFDP_PROTOCOL_VERSION
|
|
39
|
+
self.pdu_type = pdu_type
|
|
40
|
+
self.direction = direction
|
|
41
|
+
self.transmission_mode = transmission_mode
|
|
42
|
+
self.crc_flag = crc_flag
|
|
43
|
+
self.large_file_flag = large_file_flag
|
|
44
|
+
self.pdu_data_field_length = pdu_data_field_length
|
|
45
|
+
self.segmentation_control = segmentation_control
|
|
46
|
+
self.length_of_entity_ids = length_of_entity_ids
|
|
47
|
+
self.segmentation_metadata_flag = segmentation_metadata_flag
|
|
48
|
+
self.length_of_seq_number = length_of_seq_number
|
|
49
|
+
self.source_entity_id = source_entity_id
|
|
50
|
+
self.transaction_seq_number = transaction_seq_number
|
|
51
|
+
self.destination_entity_id = destination_entity_id
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def transaction_id(self) -> "TransactionId":
|
|
55
|
+
"""The owning transaction id: ``(source_entity_id, seq_number)``.
|
|
56
|
+
|
|
57
|
+
Collapses the repeated two-field reach used to key the machine map, so
|
|
58
|
+
callers write ``pdu.pdu_header.transaction_id`` instead of rebuilding
|
|
59
|
+
``(h.source_entity_id, h.transaction_seq_number)`` at each site.
|
|
60
|
+
"""
|
|
61
|
+
return (self.source_entity_id, self.transaction_seq_number)
|
|
62
|
+
|
|
63
|
+
def encode(self):
|
|
64
|
+
if self.pdu_data_field_length is None:
|
|
65
|
+
raise ValueError("PDU data field length not defined")
|
|
66
|
+
|
|
67
|
+
self.length_of_entity_ids = max(
|
|
68
|
+
get_required_octets(self.source_entity_id) - 1,
|
|
69
|
+
get_required_octets(self.destination_entity_id) - 1,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
databytes = bytes(
|
|
73
|
+
[
|
|
74
|
+
(self.version << 5)
|
|
75
|
+
+ (self.pdu_type << 4)
|
|
76
|
+
+ (self.direction << 3)
|
|
77
|
+
+ (self.transmission_mode << 2)
|
|
78
|
+
+ (self.crc_flag << 1)
|
|
79
|
+
+ (self.large_file_flag),
|
|
80
|
+
(self.pdu_data_field_length >> 8),
|
|
81
|
+
(self.pdu_data_field_length & 0xFF),
|
|
82
|
+
(self.segmentation_control << 7)
|
|
83
|
+
+ (self.length_of_entity_ids << 4)
|
|
84
|
+
+ (self.segmentation_metadata_flag << 3)
|
|
85
|
+
+ self.length_of_seq_number,
|
|
86
|
+
]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
x = []
|
|
90
|
+
for i in range(self.length_of_entity_ids + 1):
|
|
91
|
+
x.append((self.source_entity_id >> (8 * i)) & 0xFF)
|
|
92
|
+
x.reverse()
|
|
93
|
+
databytes += bytes(x)
|
|
94
|
+
|
|
95
|
+
x = []
|
|
96
|
+
for i in range(self.length_of_seq_number + 1):
|
|
97
|
+
x.append((self.transaction_seq_number >> (8 * i)) & 0xFF)
|
|
98
|
+
x.reverse()
|
|
99
|
+
databytes += bytes(x)
|
|
100
|
+
|
|
101
|
+
x = []
|
|
102
|
+
for i in range(self.length_of_entity_ids + 1):
|
|
103
|
+
x.append((self.destination_entity_id >> (8 * i)) & 0xFF)
|
|
104
|
+
x.reverse()
|
|
105
|
+
databytes += bytes(x)
|
|
106
|
+
|
|
107
|
+
return databytes
|
|
108
|
+
|
|
109
|
+
def __len__(self):
|
|
110
|
+
entity_id_field_length = 2 * (self.length_of_entity_ids + 1)
|
|
111
|
+
transaction_seq_number_field_length = self.length_of_seq_number + 1
|
|
112
|
+
return 4 + entity_id_field_length + transaction_seq_number_field_length
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def decode(cls, pdu):
|
|
116
|
+
pdu_data_field_length = int.from_bytes(pdu[1:3], "big")
|
|
117
|
+
length_of_entity_ids = (pdu[3] >> 4) & 0x07
|
|
118
|
+
length_of_seq_number = pdu[3] & 0x07
|
|
119
|
+
|
|
120
|
+
fixed_pdu_end = (
|
|
121
|
+
32 + (2 * (8 + 8 * length_of_entity_ids)) + (8 + 8 * length_of_seq_number)
|
|
122
|
+
)
|
|
123
|
+
source_entity_id = int.from_bytes(
|
|
124
|
+
pdu[4 : (32 + (8 + 8 * length_of_entity_ids)) // 8], "big"
|
|
125
|
+
)
|
|
126
|
+
transaction_seq_number = int.from_bytes(
|
|
127
|
+
pdu[
|
|
128
|
+
(32 + (8 + 8 * length_of_entity_ids)) // 8 : (
|
|
129
|
+
32 + (8 + 8 * length_of_entity_ids) + (8 + 8 * length_of_seq_number)
|
|
130
|
+
)
|
|
131
|
+
// 8
|
|
132
|
+
],
|
|
133
|
+
"big",
|
|
134
|
+
)
|
|
135
|
+
destination_entity_id = int.from_bytes(
|
|
136
|
+
pdu[
|
|
137
|
+
(32 + (8 + 8 * length_of_entity_ids) + (8 + 8 * length_of_seq_number))
|
|
138
|
+
// 8 : fixed_pdu_end // 8
|
|
139
|
+
],
|
|
140
|
+
"big",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
crc_flag = (pdu[0] >> 1) & 0x01
|
|
144
|
+
|
|
145
|
+
return cls(
|
|
146
|
+
pdu_type=(pdu[0] >> 4) & 0x01,
|
|
147
|
+
direction=(pdu[0] >> 3) & 0x01,
|
|
148
|
+
transmission_mode=(pdu[0] >> 2) & 0x01,
|
|
149
|
+
crc_flag=crc_flag,
|
|
150
|
+
large_file_flag=pdu[0] & 0x01,
|
|
151
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
152
|
+
segmentation_control=(pdu[3] >> 7) & 0x01,
|
|
153
|
+
length_of_entity_ids=length_of_entity_ids,
|
|
154
|
+
segmentation_metadata_flag=(pdu[3] >> 3) & 0x01,
|
|
155
|
+
length_of_seq_number=length_of_seq_number,
|
|
156
|
+
source_entity_id=source_entity_id,
|
|
157
|
+
transaction_seq_number=transaction_seq_number,
|
|
158
|
+
destination_entity_id=destination_entity_id,
|
|
159
|
+
)
|
cfdp/pdu/keep_alive.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from cfdp.checksum import finalize_pdu, strip_pdu_crc
|
|
2
|
+
from cfdp.constants import DirectiveCode, PduTypeCode
|
|
3
|
+
|
|
4
|
+
from .header import PduHeader
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class KeepAlivePdu:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
# pdu header parameters
|
|
11
|
+
direction=None,
|
|
12
|
+
transmission_mode=None,
|
|
13
|
+
crc_flag=0,
|
|
14
|
+
large_file_flag=0,
|
|
15
|
+
pdu_data_field_length=None,
|
|
16
|
+
segmentation_control=0,
|
|
17
|
+
length_of_entity_ids=0,
|
|
18
|
+
segmentation_metadata_flag=0,
|
|
19
|
+
length_of_seq_number=0,
|
|
20
|
+
source_entity_id=None,
|
|
21
|
+
transaction_seq_number=None,
|
|
22
|
+
destination_entity_id=None,
|
|
23
|
+
# keep alive pdu parameters
|
|
24
|
+
progress=None,
|
|
25
|
+
):
|
|
26
|
+
self.directive_code = DirectiveCode.KEEP_ALIVE
|
|
27
|
+
self.progress = progress
|
|
28
|
+
|
|
29
|
+
if progress is not None and progress > 0xFFFFFFFF:
|
|
30
|
+
large_file_flag = 1
|
|
31
|
+
|
|
32
|
+
self.pdu_header = PduHeader(
|
|
33
|
+
pdu_type=PduTypeCode.FILE_DIRECTIVE,
|
|
34
|
+
direction=direction,
|
|
35
|
+
transmission_mode=transmission_mode,
|
|
36
|
+
crc_flag=crc_flag,
|
|
37
|
+
large_file_flag=large_file_flag,
|
|
38
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
39
|
+
segmentation_control=segmentation_control,
|
|
40
|
+
length_of_entity_ids=length_of_entity_ids,
|
|
41
|
+
segmentation_metadata_flag=segmentation_metadata_flag,
|
|
42
|
+
length_of_seq_number=length_of_seq_number,
|
|
43
|
+
source_entity_id=source_entity_id,
|
|
44
|
+
transaction_seq_number=transaction_seq_number,
|
|
45
|
+
destination_entity_id=destination_entity_id,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def encode(self):
|
|
49
|
+
databytes = bytes([self.directive_code])
|
|
50
|
+
|
|
51
|
+
if not self.pdu_header.large_file_flag:
|
|
52
|
+
databytes += bytes(
|
|
53
|
+
[
|
|
54
|
+
(self.progress >> 24) & 0xFF,
|
|
55
|
+
(self.progress >> 16) & 0xFF,
|
|
56
|
+
(self.progress >> 8) & 0xFF,
|
|
57
|
+
(self.progress) & 0xFF,
|
|
58
|
+
]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
else:
|
|
62
|
+
databytes += bytes(
|
|
63
|
+
[
|
|
64
|
+
(self.progress >> 56) & 0xFF,
|
|
65
|
+
(self.progress >> 48) & 0xFF,
|
|
66
|
+
(self.progress >> 40) & 0xFF,
|
|
67
|
+
(self.progress >> 32) & 0xFF,
|
|
68
|
+
(self.progress >> 24) & 0xFF,
|
|
69
|
+
(self.progress >> 16) & 0xFF,
|
|
70
|
+
(self.progress >> 8) & 0xFF,
|
|
71
|
+
(self.progress) & 0xFF,
|
|
72
|
+
]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return finalize_pdu(self.pdu_header, databytes)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def decode(cls, pdu):
|
|
79
|
+
"""ref: CCSDS 720.2-G-3 Page 2-12"""
|
|
80
|
+
|
|
81
|
+
pdu = strip_pdu_crc(pdu)
|
|
82
|
+
pdu_header = PduHeader.decode(pdu)
|
|
83
|
+
pdu_data = pdu[len(pdu_header) :]
|
|
84
|
+
pdu_data_field_length = len(pdu_data)
|
|
85
|
+
|
|
86
|
+
progress = (
|
|
87
|
+
int.from_bytes(pdu_data[1:5], "big")
|
|
88
|
+
if not pdu_header.large_file_flag
|
|
89
|
+
else int.from_bytes(pdu_data[1:9], "big")
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return cls(
|
|
93
|
+
# pdu header parameters
|
|
94
|
+
direction=pdu_header.direction,
|
|
95
|
+
transmission_mode=pdu_header.transmission_mode,
|
|
96
|
+
crc_flag=pdu_header.crc_flag,
|
|
97
|
+
large_file_flag=pdu_header.large_file_flag,
|
|
98
|
+
pdu_data_field_length=pdu_data_field_length,
|
|
99
|
+
segmentation_control=pdu_header.segmentation_control,
|
|
100
|
+
length_of_entity_ids=pdu_header.length_of_entity_ids,
|
|
101
|
+
segmentation_metadata_flag=pdu_header.segmentation_metadata_flag,
|
|
102
|
+
length_of_seq_number=pdu_header.length_of_seq_number,
|
|
103
|
+
source_entity_id=pdu_header.source_entity_id,
|
|
104
|
+
transaction_seq_number=pdu_header.transaction_seq_number,
|
|
105
|
+
destination_entity_id=pdu_header.destination_entity_id,
|
|
106
|
+
# keep alive pdu parameters
|
|
107
|
+
progress=progress,
|
|
108
|
+
)
|