pycfdp 0.2.2__tar.gz
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.
- pycfdp-0.2.2/LICENSE.txt +19 -0
- pycfdp-0.2.2/PKG-INFO +76 -0
- pycfdp-0.2.2/README.md +58 -0
- pycfdp-0.2.2/pyproject.toml +68 -0
- pycfdp-0.2.2/setup.cfg +4 -0
- pycfdp-0.2.2/src/cfdp/__init__.py +106 -0
- pycfdp-0.2.2/src/cfdp/checksum.py +94 -0
- pycfdp-0.2.2/src/cfdp/constants.py +153 -0
- pycfdp-0.2.2/src/cfdp/core.py +895 -0
- pycfdp-0.2.2/src/cfdp/event.py +55 -0
- pycfdp-0.2.2/src/cfdp/filestore/__init__.py +2 -0
- pycfdp-0.2.2/src/cfdp/filestore/base.py +99 -0
- pycfdp-0.2.2/src/cfdp/filestore/native.py +162 -0
- pycfdp-0.2.2/src/cfdp/meta/__init__.py +3 -0
- pycfdp-0.2.2/src/cfdp/meta/datafield.py +11 -0
- pycfdp-0.2.2/src/cfdp/meta/filestore.py +127 -0
- pycfdp-0.2.2/src/cfdp/meta/message.py +659 -0
- pycfdp-0.2.2/src/cfdp/pdu/__init__.py +8 -0
- pycfdp-0.2.2/src/cfdp/pdu/ack.py +90 -0
- pycfdp-0.2.2/src/cfdp/pdu/eof.py +123 -0
- pycfdp-0.2.2/src/cfdp/pdu/filedata.py +90 -0
- pycfdp-0.2.2/src/cfdp/pdu/finished.py +99 -0
- pycfdp-0.2.2/src/cfdp/pdu/header.py +159 -0
- pycfdp-0.2.2/src/cfdp/pdu/keep_alive.py +108 -0
- pycfdp-0.2.2/src/cfdp/pdu/metadata.py +211 -0
- pycfdp-0.2.2/src/cfdp/pdu/nak.py +220 -0
- pycfdp-0.2.2/src/cfdp/pdu/prompt.py +75 -0
- pycfdp-0.2.2/src/cfdp/pure/__init__.py +68 -0
- pycfdp-0.2.2/src/cfdp/pure/config.py +141 -0
- pycfdp-0.2.2/src/cfdp/pure/indications.py +130 -0
- pycfdp-0.2.2/src/cfdp/pure/machines/__init__.py +25 -0
- pycfdp-0.2.2/src/cfdp/pure/machines/receiver1.py +433 -0
- pycfdp-0.2.2/src/cfdp/pure/machines/receiver2.py +893 -0
- pycfdp-0.2.2/src/cfdp/pure/machines/sender1.py +400 -0
- pycfdp-0.2.2/src/cfdp/pure/machines/sender2.py +774 -0
- pycfdp-0.2.2/src/cfdp/pure/outputs.py +203 -0
- pycfdp-0.2.2/src/cfdp/pure/query_port.py +48 -0
- pycfdp-0.2.2/src/cfdp/pure/transaction.py +43 -0
- pycfdp-0.2.2/src/cfdp/py.typed +0 -0
- pycfdp-0.2.2/src/cfdp/remote_entity_config.py +12 -0
- pycfdp-0.2.2/src/cfdp/shell/__init__.py +44 -0
- pycfdp-0.2.2/src/cfdp/shell/checksum_service.py +136 -0
- pycfdp-0.2.2/src/cfdp/shell/dispatcher.py +185 -0
- pycfdp-0.2.2/src/cfdp/shell/effect_executor.py +285 -0
- pycfdp-0.2.2/src/cfdp/shell/machine_registry.py +165 -0
- pycfdp-0.2.2/src/cfdp/shell/query_port.py +70 -0
- pycfdp-0.2.2/src/cfdp/shell/timer_service.py +252 -0
- pycfdp-0.2.2/src/cfdp/transaction_handle.py +50 -0
- pycfdp-0.2.2/src/cfdp/transport/__init__.py +0 -0
- pycfdp-0.2.2/src/cfdp/transport/base.py +12 -0
- pycfdp-0.2.2/src/cfdp/transport/spacepacket.py +47 -0
- pycfdp-0.2.2/src/cfdp/transport/udp.py +90 -0
- pycfdp-0.2.2/src/cfdp/transport/zmq.py +54 -0
- pycfdp-0.2.2/src/pycfdp.egg-info/PKG-INFO +76 -0
- pycfdp-0.2.2/src/pycfdp.egg-info/SOURCES.txt +96 -0
- pycfdp-0.2.2/src/pycfdp.egg-info/dependency_links.txt +1 -0
- pycfdp-0.2.2/src/pycfdp.egg-info/requires.txt +10 -0
- pycfdp-0.2.2/src/pycfdp.egg-info/top_level.txt +1 -0
- pycfdp-0.2.2/tests/test_async_checksum.py +554 -0
- pycfdp-0.2.2/tests/test_callback_api.py +241 -0
- pycfdp-0.2.2/tests/test_checksum.py +206 -0
- pycfdp-0.2.2/tests/test_chunked_copy.py +120 -0
- pycfdp-0.2.2/tests/test_class1_shell_seam.py +331 -0
- pycfdp-0.2.2/tests/test_class2_shell_seam.py +784 -0
- pycfdp-0.2.2/tests/test_cpu_yield.py +104 -0
- pycfdp-0.2.2/tests/test_direct_write_shell_seam.py +189 -0
- pycfdp-0.2.2/tests/test_e2e_suite.py +410 -0
- pycfdp-0.2.2/tests/test_entity_lifecycle.py +188 -0
- pycfdp-0.2.2/tests/test_fault_handlers.py +27 -0
- pycfdp-0.2.2/tests/test_fault_location_tlv.py +119 -0
- pycfdp-0.2.2/tests/test_file_status.py +144 -0
- pycfdp-0.2.2/tests/test_filestore_abc.py +217 -0
- pycfdp-0.2.2/tests/test_large_file_pdu.py +120 -0
- pycfdp-0.2.2/tests/test_native_filehandle.py +21 -0
- pycfdp-0.2.2/tests/test_pacing_timer.py +99 -0
- pycfdp-0.2.2/tests/test_progress_tracking.py +147 -0
- pycfdp-0.2.2/tests/test_public_api.py +70 -0
- pycfdp-0.2.2/tests/test_pure_receiver1.py +644 -0
- pycfdp-0.2.2/tests/test_pure_receiver2.py +1062 -0
- pycfdp-0.2.2/tests/test_pure_sender1.py +395 -0
- pycfdp-0.2.2/tests/test_pure_sender2.py +682 -0
- pycfdp-0.2.2/tests/test_pure_taxonomy.py +344 -0
- pycfdp-0.2.2/tests/test_put_unimplemented.py +75 -0
- pycfdp-0.2.2/tests/test_remote_entity_config.py +136 -0
- pycfdp-0.2.2/tests/test_remote_entity_timers.py +273 -0
- pycfdp-0.2.2/tests/test_series_F1.py +775 -0
- pycfdp-0.2.2/tests/test_series_F2.py +850 -0
- pycfdp-0.2.2/tests/test_series_F3.py +720 -0
- pycfdp-0.2.2/tests/test_series_F4.py +457 -0
- pycfdp-0.2.2/tests/test_spacepacket.py +59 -0
- pycfdp-0.2.2/tests/test_thread_safe_routing.py +212 -0
- pycfdp-0.2.2/tests/test_thread_safety.py +243 -0
- pycfdp-0.2.2/tests/test_timer_service.py +335 -0
- pycfdp-0.2.2/tests/test_transaction_handle.py +229 -0
- pycfdp-0.2.2/tests/test_transmission_mode_default.py +48 -0
- pycfdp-0.2.2/tests/test_transport_abc.py +108 -0
- pycfdp-0.2.2/tests/test_udp_transport_docs.py +19 -0
- pycfdp-0.2.2/tests/test_zmq.py +80 -0
pycfdp-0.2.2/LICENSE.txt
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
pycfdp-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pycfdp
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Python implementation of CCSDS File Delivery Protocol (Modernized Fork)
|
|
5
|
+
Author-email: Alex Pope <alexpopester@gmail.com>, "LibreCube (Original Codebase)" <info@librecube.org>
|
|
6
|
+
License: MIT License
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE.txt
|
|
10
|
+
Provides-Extra: zmq
|
|
11
|
+
Requires-Dist: pyzmq; extra == "zmq"
|
|
12
|
+
Provides-Extra: spacepacket
|
|
13
|
+
Requires-Dist: spacepacket; extra == "spacepacket"
|
|
14
|
+
Provides-Extra: all
|
|
15
|
+
Requires-Dist: pyzmq; extra == "all"
|
|
16
|
+
Requires-Dist: spacepacket; extra == "all"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# Python CFDP, Version 0.2
|
|
20
|
+
|
|
21
|
+
The CCSDS File Delivery Protocol (CFDP) was developed by the
|
|
22
|
+
[Consultative Committee for Space Data Systems (CCSDS)](https://public.ccsds.org).
|
|
23
|
+
The CFDP protocol provides reliable transfer of files from one endpoint to
|
|
24
|
+
another and has been designed to work well over space links that suffer
|
|
25
|
+
from outtakes and long delays. The basic operation of CFDP is to transfer a
|
|
26
|
+
file from a sender to a receiver (both referred to as CFDP entities). The
|
|
27
|
+
sender and receiver must be configured and running at the same time to perform
|
|
28
|
+
a file transfer. It can be used to perform space to ground, ground to space,
|
|
29
|
+
space to space, and ground to ground file transfers. For example it can be used
|
|
30
|
+
to transfer science data from satellite over mission control to the science
|
|
31
|
+
center in an automated fashion with minimal to no human intervention.
|
|
32
|
+
|
|
33
|
+
This Python module is an implementation of the CCSDS File Delivery Protocol.
|
|
34
|
+
It supports all features as outlined in the latest version of the [CFDP Blue Book](docs/727x0b5.pdf). These are:
|
|
35
|
+
|
|
36
|
+
- Class 1 (unacknowledged) file transfer
|
|
37
|
+
- Class 2 (acknowledged) file transfer
|
|
38
|
+
- Filestore requests
|
|
39
|
+
- Proxy operations
|
|
40
|
+
- Directory listing request
|
|
41
|
+
- Native filestore implementation
|
|
42
|
+
- UDP as default transport layer (optional ZMQ)
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
Clone the repository and then install via pip:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
pip install cfdp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> Depending on the transport layer to use with CFDP, one needs to install additional dependencies.
|
|
53
|
+
|
|
54
|
+
## Documentation
|
|
55
|
+
|
|
56
|
+
Find the detailed documentation [here](docs/README.md).
|
|
57
|
+
|
|
58
|
+
## Tests
|
|
59
|
+
|
|
60
|
+
The protocol is tested for cross-support as outlined in CCSDS CFDP Yellow Book.
|
|
61
|
+
See [tests/README.md](tests/README.md) for details.
|
|
62
|
+
|
|
63
|
+
## Contribute
|
|
64
|
+
|
|
65
|
+
To learn more on how to successfully contribute please read the contributing
|
|
66
|
+
information in the [LibreCube documentation](https://librecube.gitlab.io/).
|
|
67
|
+
|
|
68
|
+
## Support
|
|
69
|
+
|
|
70
|
+
If you are having issues, please let us know. Reach us at
|
|
71
|
+
[Matrix](https://app.element.io/#/room/#librecube.org:matrix.org)
|
|
72
|
+
or via [Email](mailto:info@librecube.org).
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
The project is licensed under the MIT license. See the [LICENSE](./LICENSE.txt) file for details.
|
pycfdp-0.2.2/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Python CFDP, Version 0.2
|
|
2
|
+
|
|
3
|
+
The CCSDS File Delivery Protocol (CFDP) was developed by the
|
|
4
|
+
[Consultative Committee for Space Data Systems (CCSDS)](https://public.ccsds.org).
|
|
5
|
+
The CFDP protocol provides reliable transfer of files from one endpoint to
|
|
6
|
+
another and has been designed to work well over space links that suffer
|
|
7
|
+
from outtakes and long delays. The basic operation of CFDP is to transfer a
|
|
8
|
+
file from a sender to a receiver (both referred to as CFDP entities). The
|
|
9
|
+
sender and receiver must be configured and running at the same time to perform
|
|
10
|
+
a file transfer. It can be used to perform space to ground, ground to space,
|
|
11
|
+
space to space, and ground to ground file transfers. For example it can be used
|
|
12
|
+
to transfer science data from satellite over mission control to the science
|
|
13
|
+
center in an automated fashion with minimal to no human intervention.
|
|
14
|
+
|
|
15
|
+
This Python module is an implementation of the CCSDS File Delivery Protocol.
|
|
16
|
+
It supports all features as outlined in the latest version of the [CFDP Blue Book](docs/727x0b5.pdf). These are:
|
|
17
|
+
|
|
18
|
+
- Class 1 (unacknowledged) file transfer
|
|
19
|
+
- Class 2 (acknowledged) file transfer
|
|
20
|
+
- Filestore requests
|
|
21
|
+
- Proxy operations
|
|
22
|
+
- Directory listing request
|
|
23
|
+
- Native filestore implementation
|
|
24
|
+
- UDP as default transport layer (optional ZMQ)
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
Clone the repository and then install via pip:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
pip install cfdp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> Depending on the transport layer to use with CFDP, one needs to install additional dependencies.
|
|
35
|
+
|
|
36
|
+
## Documentation
|
|
37
|
+
|
|
38
|
+
Find the detailed documentation [here](docs/README.md).
|
|
39
|
+
|
|
40
|
+
## Tests
|
|
41
|
+
|
|
42
|
+
The protocol is tested for cross-support as outlined in CCSDS CFDP Yellow Book.
|
|
43
|
+
See [tests/README.md](tests/README.md) for details.
|
|
44
|
+
|
|
45
|
+
## Contribute
|
|
46
|
+
|
|
47
|
+
To learn more on how to successfully contribute please read the contributing
|
|
48
|
+
information in the [LibreCube documentation](https://librecube.gitlab.io/).
|
|
49
|
+
|
|
50
|
+
## Support
|
|
51
|
+
|
|
52
|
+
If you are having issues, please let us know. Reach us at
|
|
53
|
+
[Matrix](https://app.element.io/#/room/#librecube.org:matrix.org)
|
|
54
|
+
or via [Email](mailto:info@librecube.org).
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
The project is licensed under the MIT license. See the [LICENSE](./LICENSE.txt) file for details.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pycfdp"
|
|
3
|
+
version = "0.2.2"
|
|
4
|
+
requires-python = ">=3.11"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Alex Pope", email = "alexpopester@gmail.com" },
|
|
7
|
+
{ name = "LibreCube (Original Codebase)", email = "info@librecube.org" },
|
|
8
|
+
]
|
|
9
|
+
description = "Python implementation of CCSDS File Delivery Protocol (Modernized Fork)"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = { text = "MIT License" }
|
|
12
|
+
dependencies = []
|
|
13
|
+
|
|
14
|
+
[project.optional-dependencies]
|
|
15
|
+
zmq = ["pyzmq"]
|
|
16
|
+
spacepacket = ["spacepacket"]
|
|
17
|
+
all = ["pyzmq", "spacepacket"]
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["setuptools>=68"]
|
|
21
|
+
build-backend = "setuptools.build_meta"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["src"]
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = [
|
|
28
|
+
"pytest>=8",
|
|
29
|
+
"pyzmq",
|
|
30
|
+
"spacepacket",
|
|
31
|
+
"ruff>=0.16",
|
|
32
|
+
"mypy>=1.11",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[tool.pytest.ini_options]
|
|
36
|
+
testpaths = ["tests"]
|
|
37
|
+
pythonpath = ["tests"]
|
|
38
|
+
|
|
39
|
+
[tool.mypy]
|
|
40
|
+
# Permissive by design (issue: finish what py.typed started). Default mypy does
|
|
41
|
+
# not check the bodies of un-annotated functions, so this gate validates the
|
|
42
|
+
# annotations that ARE present (the machine/shell seam) without demanding
|
|
43
|
+
# annotations across the whole codebase. Tighten incrementally as annotations
|
|
44
|
+
# spread (e.g. enable check_untyped_defs, then disallow_untyped_defs per-module).
|
|
45
|
+
python_version = "3.11"
|
|
46
|
+
files = ["src/cfdp"]
|
|
47
|
+
|
|
48
|
+
[[tool.mypy.overrides]]
|
|
49
|
+
# Optional transport deps ship no type stubs / py.typed marker.
|
|
50
|
+
module = ["spacepacket.*", "zmq.*"]
|
|
51
|
+
ignore_missing_imports = true
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
target-version = "py311"
|
|
55
|
+
src = ["src", "tests"]
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
# Correctness (F), pycodestyle (E), import sorting (I), pyupgrade (UP).
|
|
59
|
+
select = ["E", "F", "I", "UP"]
|
|
60
|
+
ignore = [
|
|
61
|
+
"E501", # line length is owned by the formatter
|
|
62
|
+
"UP031", # legacy %-formatting in logging calls; harmless, deferred
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint.per-file-ignores]
|
|
66
|
+
# __init__.py deliberately re-exports the public API (F401/F403) and sets up
|
|
67
|
+
# the package logger before imports (E402).
|
|
68
|
+
"__init__.py" = ["F401", "F403", "E402"]
|
pycfdp-0.2.2/setup.cfg
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
|
+
]
|
|
@@ -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
|
|
@@ -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
|
+
}
|