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.
Files changed (53) hide show
  1. cfdp/__init__.py +106 -0
  2. cfdp/checksum.py +94 -0
  3. cfdp/constants.py +153 -0
  4. cfdp/core.py +895 -0
  5. cfdp/event.py +55 -0
  6. cfdp/filestore/__init__.py +2 -0
  7. cfdp/filestore/base.py +99 -0
  8. cfdp/filestore/native.py +162 -0
  9. cfdp/meta/__init__.py +3 -0
  10. cfdp/meta/datafield.py +11 -0
  11. cfdp/meta/filestore.py +127 -0
  12. cfdp/meta/message.py +659 -0
  13. cfdp/pdu/__init__.py +8 -0
  14. cfdp/pdu/ack.py +90 -0
  15. cfdp/pdu/eof.py +123 -0
  16. cfdp/pdu/filedata.py +90 -0
  17. cfdp/pdu/finished.py +99 -0
  18. cfdp/pdu/header.py +159 -0
  19. cfdp/pdu/keep_alive.py +108 -0
  20. cfdp/pdu/metadata.py +211 -0
  21. cfdp/pdu/nak.py +220 -0
  22. cfdp/pdu/prompt.py +75 -0
  23. cfdp/pure/__init__.py +68 -0
  24. cfdp/pure/config.py +141 -0
  25. cfdp/pure/indications.py +130 -0
  26. cfdp/pure/machines/__init__.py +25 -0
  27. cfdp/pure/machines/receiver1.py +433 -0
  28. cfdp/pure/machines/receiver2.py +893 -0
  29. cfdp/pure/machines/sender1.py +400 -0
  30. cfdp/pure/machines/sender2.py +774 -0
  31. cfdp/pure/outputs.py +203 -0
  32. cfdp/pure/query_port.py +48 -0
  33. cfdp/pure/transaction.py +43 -0
  34. cfdp/py.typed +0 -0
  35. cfdp/remote_entity_config.py +12 -0
  36. cfdp/shell/__init__.py +44 -0
  37. cfdp/shell/checksum_service.py +136 -0
  38. cfdp/shell/dispatcher.py +185 -0
  39. cfdp/shell/effect_executor.py +285 -0
  40. cfdp/shell/machine_registry.py +165 -0
  41. cfdp/shell/query_port.py +70 -0
  42. cfdp/shell/timer_service.py +252 -0
  43. cfdp/transaction_handle.py +50 -0
  44. cfdp/transport/__init__.py +0 -0
  45. cfdp/transport/base.py +12 -0
  46. cfdp/transport/spacepacket.py +47 -0
  47. cfdp/transport/udp.py +90 -0
  48. cfdp/transport/zmq.py +54 -0
  49. pycfdp-0.2.2.dist-info/METADATA +76 -0
  50. pycfdp-0.2.2.dist-info/RECORD +53 -0
  51. pycfdp-0.2.2.dist-info/WHEEL +5 -0
  52. pycfdp-0.2.2.dist-info/licenses/LICENSE.txt +19 -0
  53. 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
+ )