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/__init__.py ADDED
@@ -0,0 +1,106 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
5
+ from .constants import (
6
+ DEFAULT_ACK_TIMER_EXPIRATION_LIMIT,
7
+ DEFAULT_ACK_TIMER_LIMIT,
8
+ DEFAULT_FAULT_HANDLERS,
9
+ DEFAULT_INACTIVITY_TIMEOUT,
10
+ DEFAULT_MAX_FILE_SEGMENT_LEN,
11
+ DEFAULT_NAK_TIMER_EXPIRATION_LIMIT,
12
+ DEFAULT_NAK_TIMER_INTERVAL,
13
+ ActionCode,
14
+ ChecksumType,
15
+ ConditionCode,
16
+ DeliveryCode,
17
+ Direction,
18
+ FaultHandlerAction,
19
+ FileStatus,
20
+ MachineState,
21
+ TransactionStatus,
22
+ TransmissionMode,
23
+ )
24
+ from .core import CfdpEntity
25
+ from .event import Event, EventType
26
+ from .meta import process_user_message
27
+ from .meta.filestore import FilestoreRequest
28
+ from .meta.message import (
29
+ DirectoryListingRequest,
30
+ DirectoryListingResponse,
31
+ MessageToUser,
32
+ OriginatingTransactionId,
33
+ ProxyPutCancel,
34
+ ProxyPutRequest,
35
+ RemoteResumeRequest,
36
+ RemoteResumeResponse,
37
+ RemoteSuspendRequest,
38
+ RemoteSuspendResponse,
39
+ UnrecognizedMessage,
40
+ )
41
+ from .pdu import (
42
+ AckPdu,
43
+ EofPdu,
44
+ FiledataPdu,
45
+ FinishedPdu,
46
+ KeepAlivePdu,
47
+ MetadataPdu,
48
+ NakPdu,
49
+ PduHeader,
50
+ PromptPdu,
51
+ )
52
+ from .pure.transaction import Transaction
53
+ from .remote_entity_config import RemoteEntityConfig
54
+ from .transaction_handle import TransactionHandle
55
+
56
+ __all__ = [
57
+ # Core public API
58
+ "CfdpEntity",
59
+ "RemoteEntityConfig",
60
+ "TransactionHandle",
61
+ # Constants / enums used in the public interface
62
+ "TransmissionMode",
63
+ "Direction",
64
+ "ConditionCode",
65
+ "MachineState",
66
+ "DeliveryCode",
67
+ "FileStatus",
68
+ "TransactionStatus",
69
+ "FaultHandlerAction",
70
+ "ChecksumType",
71
+ "ActionCode",
72
+ "FilestoreRequest",
73
+ "DEFAULT_FAULT_HANDLERS",
74
+ "DEFAULT_INACTIVITY_TIMEOUT",
75
+ "DEFAULT_ACK_TIMER_LIMIT",
76
+ "DEFAULT_ACK_TIMER_EXPIRATION_LIMIT",
77
+ "DEFAULT_NAK_TIMER_INTERVAL",
78
+ "DEFAULT_NAK_TIMER_EXPIRATION_LIMIT",
79
+ "DEFAULT_MAX_FILE_SEGMENT_LEN",
80
+ # PDU types (needed for advanced use)
81
+ "PduHeader",
82
+ "MetadataPdu",
83
+ "FiledataPdu",
84
+ "EofPdu",
85
+ "FinishedPdu",
86
+ "NakPdu",
87
+ "AckPdu",
88
+ "KeepAlivePdu",
89
+ "PromptPdu",
90
+ # Meta / message types
91
+ "MessageToUser",
92
+ "ProxyPutRequest",
93
+ "ProxyPutCancel",
94
+ "DirectoryListingRequest",
95
+ "DirectoryListingResponse",
96
+ "OriginatingTransactionId",
97
+ "RemoteSuspendRequest",
98
+ "RemoteSuspendResponse",
99
+ "RemoteResumeRequest",
100
+ "RemoteResumeResponse",
101
+ "UnrecognizedMessage",
102
+ # Internal types occasionally accessed in tests / advanced usage
103
+ "Event",
104
+ "EventType",
105
+ "Transaction",
106
+ ]
cfdp/checksum.py ADDED
@@ -0,0 +1,94 @@
1
+ import binascii
2
+ from collections.abc import Iterable
3
+
4
+
5
+ class UnsupportedChecksumType(Exception):
6
+ pass
7
+
8
+
9
+ class PduCrcError(Exception):
10
+ pass
11
+
12
+
13
+ def _build_crc32c_table():
14
+ poly = 0x82F63B78 # reversed Castagnoli polynomial
15
+ table = []
16
+ for i in range(256):
17
+ crc = i
18
+ for _ in range(8):
19
+ crc = (crc >> 1) ^ poly if crc & 1 else crc >> 1
20
+ table.append(crc)
21
+ return table
22
+
23
+
24
+ _CRC32C_TABLE = _build_crc32c_table()
25
+
26
+
27
+ def calculate_checksum(data_iter: Iterable[bytes], checksum_type: int) -> int:
28
+ """Compute a CFDP file checksum over an iterable of byte chunks."""
29
+ if checksum_type == 0: # Modular (CCSDS 727.0-B-5 Annex F)
30
+ checksum = 0
31
+ buf = b""
32
+ for chunk in data_iter:
33
+ buf += chunk
34
+ n = (len(buf) // 4) * 4
35
+ for i in range(0, n, 4):
36
+ checksum += int.from_bytes(buf[i : i + 4], "big")
37
+ buf = buf[n:]
38
+ if buf:
39
+ padded = buf + b"\x00" * (4 - len(buf))
40
+ checksum += int.from_bytes(padded, "big")
41
+ return checksum % (2**32)
42
+
43
+ elif checksum_type == 2: # CRC-32C (Castagnoli)
44
+ crc = 0xFFFFFFFF
45
+ for chunk in data_iter:
46
+ for byte in chunk:
47
+ crc = (crc >> 8) ^ _CRC32C_TABLE[(crc ^ byte) & 0xFF]
48
+ return crc ^ 0xFFFFFFFF
49
+
50
+ elif checksum_type == 3: # IEEE CRC-32
51
+ crc = 0
52
+ for chunk in data_iter:
53
+ crc = binascii.crc32(chunk, crc)
54
+ return crc & 0xFFFFFFFF
55
+
56
+ elif checksum_type == 15: # Null
57
+ return 0
58
+
59
+ else:
60
+ raise UnsupportedChecksumType(f"Unsupported checksum type: {checksum_type}")
61
+
62
+
63
+ def crc16_ccitt(data: bytes) -> int:
64
+ """CRC-16/CCITT-FALSE (poly=0x1021, init=0xFFFF) used for PDU integrity."""
65
+ crc = 0xFFFF
66
+ for byte in data:
67
+ crc ^= byte << 8
68
+ for _ in range(8):
69
+ crc = (crc << 1) ^ 0x1021 if crc & 0x8000 else crc << 1
70
+ crc &= 0xFFFF
71
+ return crc
72
+
73
+
74
+ def strip_pdu_crc(pdu: bytes) -> bytes:
75
+ """If crc_flag is set, verify the trailing 2-byte CRC and return the PDU without it."""
76
+ if not (pdu[0] >> 1) & 0x01:
77
+ return pdu
78
+ received = int.from_bytes(pdu[-2:], "big")
79
+ computed = crc16_ccitt(pdu[:-2])
80
+ if received != computed:
81
+ raise PduCrcError(
82
+ f"PDU CRC mismatch: computed {computed:#06x}, received {received:#06x}"
83
+ )
84
+ return pdu[:-2]
85
+
86
+
87
+ def finalize_pdu(pdu_header, databytes: bytes) -> bytes:
88
+ """Set pdu_data_field_length, encode header+data, append CRC-16 if crc_flag=1."""
89
+ crc_flag = pdu_header.crc_flag
90
+ pdu_header.pdu_data_field_length = len(databytes) + (2 if crc_flag else 0)
91
+ pdu_bytes = pdu_header.encode() + databytes
92
+ if crc_flag:
93
+ pdu_bytes += crc16_ccitt(pdu_bytes).to_bytes(2, "big")
94
+ return pdu_bytes
cfdp/constants.py ADDED
@@ -0,0 +1,153 @@
1
+ class ProtocolVersion:
2
+ VERSION_1 = 0
3
+ VERSION_2 = 1
4
+
5
+
6
+ class TransmissionMode:
7
+ ACKNOWLEDGED = 0
8
+ UNACKNOWLEDGED = 1
9
+
10
+
11
+ class PduTypeCode:
12
+ FILE_DIRECTIVE = 0
13
+ FILE_DATA = 1
14
+
15
+
16
+ class Direction:
17
+ TOWARD_RECEIVER = 0
18
+ TOWARD_SENDER = 1
19
+
20
+
21
+ class DirectiveCode:
22
+ EOF = 0x04
23
+ FINISHED = 0x05
24
+ ACK = 0x06
25
+ METADATA = 0x07
26
+ NAK = 0x08
27
+ PROMPT = 0x09
28
+ KEEP_ALIVE = 0x0C
29
+
30
+
31
+ class DirectiveSubTypeCode:
32
+ ACK_OTHERS = 0x00
33
+ ACK_FINISHED = 0x01
34
+
35
+
36
+ class ConditionCode:
37
+ NO_ERROR = 0
38
+ POSITIVE_ACK_LIMIT_REACHED = 1
39
+ KEEP_ALIVE_LIMIT_REACHED = 2
40
+ INVALID_TRANSMISSION_MODE = 3
41
+ FILESTORE_REJECTION = 4
42
+ FILE_CHECKSUM_FAILURE = 5
43
+ FILE_SIZE_ERROR = 6
44
+ NAK_LIMIT_REACHED = 7
45
+ INACTIVITY_DETECTED = 8
46
+ INVALID_FILE_STRUCTURE = 9
47
+ CHECK_LIMIT_REACHED = 10
48
+ UNSUPPORTED_CHECKSUM_TYPE = 11
49
+ SUSPEND_REQUEST_RECEIVED = 14
50
+ CANCEL_REQUEST_RECEIVED = 15
51
+
52
+
53
+ class MachineState:
54
+ SEND_METADATA = "SEND_METADATA"
55
+ SEND_FILE = "SEND_FILE"
56
+ SEND_EOF = "SEND_EOF"
57
+ TRANSACTION_CANCELLED = "TRANSACTION_CANCELLED"
58
+ WAIT_FOR_MD = "WAIT_FOR_MD"
59
+ WAIT_FOR_EOF = "WAIT_FOR_EOF"
60
+ GET_MISSING_DATA = "GET_MISSING_DATA"
61
+ SEND_FINISHED = "SEND_FINISHED"
62
+ COMPLETED = "COMPLETED"
63
+
64
+
65
+ class DeliveryCode:
66
+ DATA_COMPLETE = 0
67
+ DATA_INCOMPLETE = 1
68
+
69
+
70
+ class FileStatus:
71
+ DISCARDED_DELIBERATELY = 0
72
+ DISCARDED_FILESTOR_REJECTION = 1
73
+ RETAINED_SUCCESSFULLY = 2
74
+ UNREPORTED = 3
75
+
76
+
77
+ class TransactionStatus:
78
+ UNDEFINED = 0
79
+ ACTIVE = 1
80
+ TERMINATED = 2
81
+ UNRECOGNIZED = 3
82
+
83
+
84
+ class ActionCode:
85
+ CREATE_FILE = 0
86
+ DELETE_FILE = 1
87
+ RENAME_FILE = 2
88
+ APPEND_FILE = 3
89
+ REPLACE_FILE = 4
90
+ CREATE_DIRECTORY = 5
91
+ REMOVE_DIRECTORY = 6
92
+ DENY_FILE = 7 # delete if present
93
+ DENY_DIRECTORY = 8 # remove if present
94
+
95
+
96
+ class TypeFieldCode:
97
+ FILESTORE_REQUEST = 0x00
98
+ FILESTORE_RESPONSE = 0x01
99
+ MESSAGE_TO_USER = 0x02
100
+ FAULT_HANDLER_OVERRIDES = 0x04
101
+ FLOW_LABEL = 0x05
102
+ ENTITY_ID = 0x06
103
+
104
+
105
+ class MessageType:
106
+ PROXY_PUT_REQUEST = 0x00
107
+ PROXY_PUT_CANCEL = 0x09
108
+ ORIGINATING_TRANSACTION_ID = 0x0A
109
+ DIRECTORY_LISTING_REQUEST = 0x10
110
+ DIRECTORY_LISTING_RESPONSE = 0x11
111
+ REMOTE_SUSPEND_REQUEST = 0x30
112
+ REMOTE_SUSPEND_RESPONSE = 0x31
113
+ REMOTE_RESUME_REQUEST = 0x38
114
+ REMOTE_RESUME_RESPONSE = 0x39
115
+
116
+
117
+ class FaultHandlerAction:
118
+ CANCEL = "CANCEL"
119
+ SUSPEND = "SUSPEND"
120
+ IGNORE = "IGNORE"
121
+ ABANDON = "ABANDON"
122
+
123
+
124
+ class ChecksumType:
125
+ MODULAR = 0
126
+ PROXIMITY1 = 1
127
+ CRC32C = 2
128
+ IEEE = 3
129
+ NULL = 15
130
+
131
+
132
+ DEFAULT_MAX_FILE_SEGMENT_LEN = 800
133
+ DEFAULT_INACTIVITY_TIMEOUT = 30
134
+ DEFAULT_ACK_TIMER_LIMIT = 5
135
+ DEFAULT_ACK_TIMER_EXPIRATION_LIMIT = 3
136
+ DEFAULT_NAK_TIMER_INTERVAL = 5
137
+ DEFAULT_NAK_TIMER_EXPIRATION_LIMIT = 5
138
+ DEFAULT_FAULT_HANDLERS = {
139
+ ConditionCode.NO_ERROR: FaultHandlerAction.IGNORE,
140
+ ConditionCode.POSITIVE_ACK_LIMIT_REACHED: FaultHandlerAction.CANCEL,
141
+ ConditionCode.KEEP_ALIVE_LIMIT_REACHED: FaultHandlerAction.CANCEL,
142
+ ConditionCode.INVALID_TRANSMISSION_MODE: FaultHandlerAction.CANCEL,
143
+ ConditionCode.FILESTORE_REJECTION: FaultHandlerAction.CANCEL,
144
+ ConditionCode.FILE_CHECKSUM_FAILURE: FaultHandlerAction.CANCEL,
145
+ ConditionCode.FILE_SIZE_ERROR: FaultHandlerAction.CANCEL,
146
+ ConditionCode.NAK_LIMIT_REACHED: FaultHandlerAction.CANCEL,
147
+ ConditionCode.INACTIVITY_DETECTED: FaultHandlerAction.CANCEL,
148
+ ConditionCode.INVALID_FILE_STRUCTURE: FaultHandlerAction.CANCEL,
149
+ ConditionCode.CHECK_LIMIT_REACHED: FaultHandlerAction.CANCEL,
150
+ ConditionCode.UNSUPPORTED_CHECKSUM_TYPE: FaultHandlerAction.CANCEL,
151
+ ConditionCode.SUSPEND_REQUEST_RECEIVED: FaultHandlerAction.IGNORE,
152
+ ConditionCode.CANCEL_REQUEST_RECEIVED: FaultHandlerAction.IGNORE,
153
+ }