Compare commits
55 Commits
Author | SHA1 | Date | |
---|---|---|---|
d82cecbe6e | |||
14820d63ac
|
|||
3d27711abb
|
|||
1d5db0951e
|
|||
d67987745d | |||
bde03fc9c0
|
|||
be1ac09515 | |||
d1fde6f1e5 | |||
caa843fb1a | |||
11f7ed8436 | |||
88b0c33632 | |||
dd74f6c3ca | |||
783bdd297a
|
|||
bf399c3d91 | |||
06a058fc4d
|
|||
b464182df7
|
|||
a30bccc995
|
|||
980242404a
|
|||
a8545211e8
|
|||
fe439b4d3c | |||
f63f4b9193
|
|||
8d28b321d4
|
|||
8b45dd8bff
|
|||
1860b754ce
|
|||
68138c6e79
|
|||
baa1d20556 | |||
1faecb09de
|
|||
1ad621e630
|
|||
5fc3d8de99
|
|||
224328cc85
|
|||
649deac81b
|
|||
7fa1196633
|
|||
1b0a7aeabd
|
|||
412d67494d
|
|||
25377ccfa2
|
|||
6657bf072c
|
|||
7f79ef6c12
|
|||
d72b7d9d66
|
|||
c687b1411c
|
|||
c2bed714dc
|
|||
f9f8f9481f
|
|||
8d6ca602f2
|
|||
c0bd5f572c
|
|||
25c7decb1e
|
|||
cc7b1d3331
|
|||
948f3a1a41
|
|||
8743f59b56
|
|||
2cc4a18c1e | |||
55624f0447 | |||
9841076699
|
|||
f1876681fd | |||
14b558b7f7
|
|||
54a7c3566f
|
|||
011be9837e
|
|||
88161ed125
|
18
.flake8
18
.flake8
@ -1,18 +0,0 @@
|
|||||||
[flake8]
|
|
||||||
max-line-length = 100
|
|
||||||
ignore = D203, W503
|
|
||||||
per-file-ignores =
|
|
||||||
*/__init__.py: F401
|
|
||||||
exclude =
|
|
||||||
.git,
|
|
||||||
__pycache__,
|
|
||||||
docs/conf.py,
|
|
||||||
deps
|
|
||||||
old,
|
|
||||||
build,
|
|
||||||
dist,
|
|
||||||
venv
|
|
||||||
max-complexity = 10
|
|
||||||
extend-ignore =
|
|
||||||
# See https://github.com/PyCQA/pycodestyle/issues/373
|
|
||||||
E203,
|
|
24
.run/CFDP Downlink Test Large.run.xml
Normal file
24
.run/CFDP Downlink Test Large.run.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="CFDP Downlink Test Large" type="PythonConfigurationType" factoryName="Python" folderName="CFDP">
|
||||||
|
<module name="tmtc" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="PARENT_ENVS" value="true" />
|
||||||
|
<envs>
|
||||||
|
<env name="PYTHONUNBUFFERED" value="1" />
|
||||||
|
</envs>
|
||||||
|
<option name="SDK_HOME" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
<option name="IS_MODULE_SDK" value="true" />
|
||||||
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||||
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||||
|
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||||
|
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtcc.py" />
|
||||||
|
<option name="PARAMETERS" value="cfdp -p /tmp/obsw_update.bin /tmp/obsw_update_copy.bin -d 0.1" />
|
||||||
|
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||||
|
<option name="EMULATE_TERMINAL" value="false" />
|
||||||
|
<option name="MODULE_MODE" value="false" />
|
||||||
|
<option name="REDIRECT_INPUT" value="false" />
|
||||||
|
<option name="INPUT_FILE" value="" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
24
.run/CFDP Downlink Test Larger.run.xml
Normal file
24
.run/CFDP Downlink Test Larger.run.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="CFDP Downlink Test Larger" type="PythonConfigurationType" factoryName="Python" folderName="CFDP">
|
||||||
|
<module name="tmtc" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="PARENT_ENVS" value="true" />
|
||||||
|
<envs>
|
||||||
|
<env name="PYTHONUNBUFFERED" value="1" />
|
||||||
|
</envs>
|
||||||
|
<option name="SDK_HOME" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
<option name="IS_MODULE_SDK" value="true" />
|
||||||
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||||
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||||
|
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||||
|
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtcc.py" />
|
||||||
|
<option name="PARAMETERS" value="cfdp -p /tmp/fake_5kb.bin /tmp/fake_5kb_copy.bin -d 0.1" />
|
||||||
|
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||||
|
<option name="EMULATE_TERMINAL" value="false" />
|
||||||
|
<option name="MODULE_MODE" value="false" />
|
||||||
|
<option name="REDIRECT_INPUT" value="false" />
|
||||||
|
<option name="INPUT_FILE" value="" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
24
.run/CFDP Downlink Test.run.xml
Normal file
24
.run/CFDP Downlink Test.run.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="CFDP Downlink Test" type="PythonConfigurationType" factoryName="Python" folderName="CFDP">
|
||||||
|
<module name="tmtc" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="PARENT_ENVS" value="true" />
|
||||||
|
<envs>
|
||||||
|
<env name="PYTHONUNBUFFERED" value="1" />
|
||||||
|
</envs>
|
||||||
|
<option name="SDK_HOME" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
<option name="IS_MODULE_SDK" value="true" />
|
||||||
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||||
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||||
|
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||||
|
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtcc.py" />
|
||||||
|
<option name="PARAMETERS" value="cfdp -p /tmp/hello.txt /tmp/hello2.txt -d 0.1" />
|
||||||
|
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||||
|
<option name="EMULATE_TERMINAL" value="false" />
|
||||||
|
<option name="MODULE_MODE" value="false" />
|
||||||
|
<option name="REDIRECT_INPUT" value="false" />
|
||||||
|
<option name="INPUT_FILE" value="" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
18
CHANGELOG.md
18
CHANGELOG.md
@ -10,6 +10,24 @@ list yields a list of all related PRs for each release.
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
# [v5.7.0] 2023-10-10
|
||||||
|
|
||||||
|
- `tmtccmd` v6.0.0
|
||||||
|
- `spacepackets` v18.0.0
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Power controller commands.
|
||||||
|
|
||||||
|
# [v5.6.0] 2023-09-14
|
||||||
|
|
||||||
|
- `tmtccmd` v6.0.0
|
||||||
|
- `spacepackets` v18.0.0
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- CFDP file downlink support.
|
||||||
|
|
||||||
# [v5.5.1] 2023-09-12
|
# [v5.5.1] 2023-09-12
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
43
README.md
43
README.md
@ -4,7 +4,7 @@ This application can be used to test the EIVE On-Board Software. Furthermore, it
|
|||||||
also be used to retrieve all sorts of telemetry data like housekeeping data.
|
also be used to retrieve all sorts of telemetry data like housekeeping data.
|
||||||
|
|
||||||
It is recommended to use this application with a virtual environment.
|
It is recommended to use this application with a virtual environment.
|
||||||
The [virtual environemnt](#venv) chapter describes how to set one up. The [requirements](#reqs)
|
The [virtual environment](#venv) chapter describes how to set one up. The [requirements](#reqs)
|
||||||
describes how to install all required packages.
|
describes how to install all required packages.
|
||||||
|
|
||||||
The configuration file can currently be found at `tmtc_conf.json`. It caches settings
|
The configuration file can currently be found at `tmtc_conf.json`. It caches settings
|
||||||
@ -61,42 +61,57 @@ Run GUI mode
|
|||||||
# <a id="reqs"></a> Install requirements
|
# <a id="reqs"></a> Install requirements
|
||||||
|
|
||||||
There are two ways to install the requirements. One is to install the primary dependency
|
There are two ways to install the requirements. One is to install the primary dependency
|
||||||
`tmtccmd` interactively. This is the recommended way
|
`tmtccmd` interactively.
|
||||||
|
|
||||||
Assuming you are running in a virtual environment:
|
## Installing via PyPI
|
||||||
|
|
||||||
1. Install `tmtccmd` for virtual environment. `-e` for interactive installation.
|
It is recommended to install `eive-tmtc` itself interactively, which also installs
|
||||||
|
all required dependencies.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd deps/tmtccmd
|
pip install -e .
|
||||||
pip install -e .[gui]
|
```
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively you can also install the packages from PyPI completely, but the risk of
|
If you only want to install all dependencies:
|
||||||
incompatibilities will be high there
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Install interactively
|
||||||
|
|
||||||
|
Clone the dependency first inside the `deps` folder
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd deps
|
||||||
|
./install_tmtccmd.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can install `tmtccmd` interactively
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd tmtccmd
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
# Run Linter
|
# Run Linter
|
||||||
|
|
||||||
Can be used to quickly check validity of script. Install `flake8` first
|
Can be used to quickly check validity of script. Install `flake8` first
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 -m pip install flake8
|
python3 -m pip install ruff
|
||||||
```
|
```
|
||||||
|
|
||||||
or on Windows
|
or on Windows
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
py -m pip install flake8
|
py -m pip install ruff
|
||||||
```
|
```
|
||||||
|
|
||||||
and then run the `lint.py` script
|
and then run it
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./lint.py
|
ruff .
|
||||||
```
|
```
|
||||||
|
|
||||||
# Run Auto-Formatter
|
# Run Auto-Formatter
|
||||||
|
0
eive_tmtc/cfdp/__init__.py
Normal file
0
eive_tmtc/cfdp/__init__.py
Normal file
20
eive_tmtc/cfdp/fault_handler.py
Normal file
20
eive_tmtc/cfdp/fault_handler.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from spacepackets.cfdp import ConditionCode
|
||||||
|
from tmtccmd.cfdp.mib import DefaultFaultHandlerBase
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EiveCfdpFaultHandler(DefaultFaultHandlerBase):
|
||||||
|
def notice_of_suspension_cb(self, cond: ConditionCode):
|
||||||
|
_LOGGER.info(f"Received notice of suspension: {cond!r}")
|
||||||
|
|
||||||
|
def notice_of_cancellation_cb(self, cond: ConditionCode):
|
||||||
|
_LOGGER.info(f"Received notice of cancellation: {cond!r}")
|
||||||
|
|
||||||
|
def abandoned_cb(self, cond: ConditionCode):
|
||||||
|
_LOGGER.info(f"Abandoned transaction: {cond!r}")
|
||||||
|
|
||||||
|
def ignore_cb(self, cond: ConditionCode):
|
||||||
|
_LOGGER.info(f"Ignored transaction: {cond!r}")
|
27
eive_tmtc/cfdp/tm.py
Normal file
27
eive_tmtc/cfdp/tm.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from eive_tmtc.config.definitions import CFDP_APID
|
||||||
|
from spacepackets.ccsds import SPACE_PACKET_HEADER_SIZE
|
||||||
|
from spacepackets.cfdp import PduFactory, PduType, DirectiveType
|
||||||
|
from tmtccmd.cfdp.handler import CfdpInCcsdsHandler
|
||||||
|
from tmtccmd.tm import SpecificApidHandlerBase
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CfdpInCcsdsWrapper(SpecificApidHandlerBase):
|
||||||
|
def __init__(self, cfdp_in_ccsds_handler: CfdpInCcsdsHandler):
|
||||||
|
super().__init__(CFDP_APID, None)
|
||||||
|
self.handler = cfdp_in_ccsds_handler
|
||||||
|
|
||||||
|
def handle_tm(self, packet: bytes, _user_args: any):
|
||||||
|
# Ignore the space packet header. Its only purpose is to use the same protocol and
|
||||||
|
# have a seaprate APID for space packets. If this function is called, the APID is correct.
|
||||||
|
pdu = packet[SPACE_PACKET_HEADER_SIZE:]
|
||||||
|
pdu_base = PduFactory.from_raw(pdu)
|
||||||
|
if pdu_base.pdu_type == PduType.FILE_DATA:
|
||||||
|
_LOGGER.info("Received File Data PDU")
|
||||||
|
else:
|
||||||
|
directive_type = DirectiveType(pdu_base.directive_type)
|
||||||
|
_LOGGER.info(f"Received File Directive PDU with type {directive_type!r}")
|
||||||
|
self.handler.insert_pdu_packet(pdu_base)
|
58
eive_tmtc/cfdp/user.py
Normal file
58
eive_tmtc/cfdp/user.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from spacepackets.cfdp import ConditionCode
|
||||||
|
from tmtccmd.cfdp import CfdpUserBase, TransactionId
|
||||||
|
from tmtccmd.cfdp.user import (
|
||||||
|
TransactionFinishedParams,
|
||||||
|
MetadataRecvParams,
|
||||||
|
FileSegmentRecvdParams,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class EiveCfdpUser(CfdpUserBase):
|
||||||
|
def transaction_indication(self, transaction_id: TransactionId):
|
||||||
|
_LOGGER.info(f"CFDP User: Start of File {transaction_id}")
|
||||||
|
|
||||||
|
def eof_sent_indication(self, transaction_id: TransactionId):
|
||||||
|
_LOGGER.info(f"CFDP User: EOF sent for {transaction_id}")
|
||||||
|
|
||||||
|
def transaction_finished_indication(self, params: TransactionFinishedParams):
|
||||||
|
_LOGGER.info(f"CFDP User: {params.transaction_id} finished")
|
||||||
|
_LOGGER.info(f"Delivery Code: {params.delivery_code!r}")
|
||||||
|
_LOGGER.info(f"Condition code: {params.condition_code!r}")
|
||||||
|
_LOGGER.info(f"File delivery status: {params.delivery_code!r}")
|
||||||
|
|
||||||
|
def metadata_recv_indication(self, params: MetadataRecvParams):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def file_segment_recv_indication(self, params: FileSegmentRecvdParams):
|
||||||
|
_LOGGER.info(
|
||||||
|
f"CFDP User: Received File Data PDU for {params.transaction_id} | Offset:"
|
||||||
|
f" {params.offset} | Segment Length: {params.length}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def report_indication(self, transaction_id: TransactionId, status_report: any):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def suspended_indication(
|
||||||
|
self, transaction_id: TransactionId, cond_code: ConditionCode
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def resumed_indication(self, transaction_id: TransactionId, progress: int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def fault_indication(
|
||||||
|
self, transaction_id: TransactionId, cond_code: ConditionCode, progress: int
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def abandoned_indication(
|
||||||
|
self, transaction_id: TransactionId, cond_code: ConditionCode, progress: int
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def eof_recv_indication(self, transaction_id: TransactionId):
|
||||||
|
_LOGGER.info(f"CFDP User: EOF received for {transaction_id}")
|
@ -76,3 +76,5 @@ class CustomServiceList(str, enum.Enum):
|
|||||||
SCEX = "scex"
|
SCEX = "scex"
|
||||||
TM_STORE = "tm_store"
|
TM_STORE = "tm_store"
|
||||||
SYSTEM = "system"
|
SYSTEM = "system"
|
||||||
|
PWR_CTRL = "pwr_ctrl"
|
||||||
|
EPS_SS = "eps_subsystem"
|
||||||
|
@ -99,6 +99,11 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path
|
|||||||
11301;0x2c25;SWITCH_HAS_CHANGED;INFO;Indicated that a switch state has changed P1: New switch state, 1 for on, 0 for off | P2: Switch Index;mission/power/defs.h
|
11301;0x2c25;SWITCH_HAS_CHANGED;INFO;Indicated that a switch state has changed P1: New switch state, 1 for on, 0 for off | P2: Switch Index;mission/power/defs.h
|
||||||
11302;0x2c26;SWITCHING_Q7S_DENIED;MEDIUM;No description;mission/power/defs.h
|
11302;0x2c26;SWITCHING_Q7S_DENIED;MEDIUM;No description;mission/power/defs.h
|
||||||
11303;0x2c27;FDIR_REACTION_IGNORED;MEDIUM;No description;mission/power/defs.h
|
11303;0x2c27;FDIR_REACTION_IGNORED;MEDIUM;No description;mission/power/defs.h
|
||||||
|
11304;0x2c28;DATASET_READ_FAILED;INFO;The dataset read for the inputs of the Power Controller has failed.;mission/power/defs.h
|
||||||
|
11305;0x2c29;VOLTAGE_OUT_OF_BOUNDS;HIGH;No description;mission/power/defs.h
|
||||||
|
11306;0x2c2a;TIMEDELTA_OUT_OF_BOUNDS;LOW;Time difference for Coulomb Counter was too large. P1: time in s * 10;mission/power/defs.h
|
||||||
|
11307;0x2c2b;POWER_LEVEL_LOW;HIGH;The State of Charge is below the limit for payload use. Setting Payload to faulty.;mission/power/defs.h
|
||||||
|
11308;0x2c2c;POWER_LEVEL_CRITICAL;HIGH;The State of Charge is below the limit for higher modes. Setting Reaction Wheels to faulty.;mission/power/defs.h
|
||||||
11400;0x2c88;GPIO_PULL_HIGH_FAILED;LOW;No description;mission/tcs/HeaterHandler.h
|
11400;0x2c88;GPIO_PULL_HIGH_FAILED;LOW;No description;mission/tcs/HeaterHandler.h
|
||||||
11401;0x2c89;GPIO_PULL_LOW_FAILED;LOW;No description;mission/tcs/HeaterHandler.h
|
11401;0x2c89;GPIO_PULL_LOW_FAILED;LOW;No description;mission/tcs/HeaterHandler.h
|
||||||
11402;0x2c8a;HEATER_WENT_ON;INFO;No description;mission/tcs/HeaterHandler.h
|
11402;0x2c8a;HEATER_WENT_ON;INFO;No description;mission/tcs/HeaterHandler.h
|
||||||
@ -122,6 +127,8 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path
|
|||||||
11604;0x2d54;MPSOC_HANDLER_CRC_FAILURE;LOW;PLOC reply has invalid crc;linux/payload/PlocMpsocHandler.h
|
11604;0x2d54;MPSOC_HANDLER_CRC_FAILURE;LOW;PLOC reply has invalid crc;linux/payload/PlocMpsocHandler.h
|
||||||
11605;0x2d55;MPSOC_HANDLER_SEQUENCE_COUNT_MISMATCH;LOW;Packet sequence count in received space packet does not match expected count P1: Expected sequence count P2: Received sequence count;linux/payload/PlocMpsocHandler.h
|
11605;0x2d55;MPSOC_HANDLER_SEQUENCE_COUNT_MISMATCH;LOW;Packet sequence count in received space packet does not match expected count P1: Expected sequence count P2: Received sequence count;linux/payload/PlocMpsocHandler.h
|
||||||
11606;0x2d56;MPSOC_SHUTDOWN_FAILED;HIGH;Supervisor fails to shutdown MPSoC. Requires to power off the PLOC and thus also to shutdown the supervisor.;linux/payload/PlocMpsocHandler.h
|
11606;0x2d56;MPSOC_SHUTDOWN_FAILED;HIGH;Supervisor fails to shutdown MPSoC. Requires to power off the PLOC and thus also to shutdown the supervisor.;linux/payload/PlocMpsocHandler.h
|
||||||
|
11607;0x2d57;SUPV_NOT_ON;LOW;SUPV not on for boot or shutdown process. P1: 0 for OFF transition, 1 for ON transition.;linux/payload/PlocMpsocHandler.h
|
||||||
|
11608;0x2d58;SUPV_REPLY_TIMEOUT;LOW;No description;linux/payload/PlocMpsocHandler.h
|
||||||
11701;0x2db5;SELF_TEST_I2C_FAILURE;LOW;Get self test result returns I2C failure P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA;mission/acs/ImtqHandler.h
|
11701;0x2db5;SELF_TEST_I2C_FAILURE;LOW;Get self test result returns I2C failure P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA;mission/acs/ImtqHandler.h
|
||||||
11702;0x2db6;SELF_TEST_SPI_FAILURE;LOW;Get self test result returns SPI failure. This concerns the MTM connectivity. P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA;mission/acs/ImtqHandler.h
|
11702;0x2db6;SELF_TEST_SPI_FAILURE;LOW;Get self test result returns SPI failure. This concerns the MTM connectivity. P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA;mission/acs/ImtqHandler.h
|
||||||
11703;0x2db7;SELF_TEST_ADC_FAILURE;LOW;Get self test result returns failure in measurement of current and temperature. P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA;mission/acs/ImtqHandler.h
|
11703;0x2db7;SELF_TEST_ADC_FAILURE;LOW;Get self test result returns failure in measurement of current and temperature. P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA;mission/acs/ImtqHandler.h
|
||||||
|
|
@ -136,6 +136,7 @@ ACS_SUBSYSTEM_ID = bytes([0x73, 0x01, 0x00, 0x01])
|
|||||||
PL_SUBSYSTEM_ID = bytes([0x73, 0x01, 0x00, 0x02])
|
PL_SUBSYSTEM_ID = bytes([0x73, 0x01, 0x00, 0x02])
|
||||||
TCS_SUBSYSTEM_ID = bytes([0x73, 0x01, 0x00, 0x03])
|
TCS_SUBSYSTEM_ID = bytes([0x73, 0x01, 0x00, 0x03])
|
||||||
COM_SUBSYSTEM_ID = bytes([0x73, 0x01, 0x00, 0x04])
|
COM_SUBSYSTEM_ID = bytes([0x73, 0x01, 0x00, 0x04])
|
||||||
|
EPS_SUBSYSTEM_ID = bytes([0x73, 0x01, 0x00, 0x05])
|
||||||
|
|
||||||
# Legacy names, kept for backwards compatibility
|
# Legacy names, kept for backwards compatibility
|
||||||
ACS_BOARD_ASS_ID = bytes([0x73, 0x00, 0x00, 0x01])
|
ACS_BOARD_ASS_ID = bytes([0x73, 0x00, 0x00, 0x01])
|
||||||
@ -157,6 +158,7 @@ STR_ASSEMBLY = bytes([0x73, 0x00, 0x00, 0x09])
|
|||||||
TCS_CONTROLLER = bytes([0x43, 0x40, 0x00, 0x01])
|
TCS_CONTROLLER = bytes([0x43, 0x40, 0x00, 0x01])
|
||||||
ACS_CONTROLLER = bytes([0x43, 0x00, 0x00, 0x02])
|
ACS_CONTROLLER = bytes([0x43, 0x00, 0x00, 0x02])
|
||||||
CORE_CONTROLLER_ID = bytes([0x43, 0x00, 0x00, 0x03])
|
CORE_CONTROLLER_ID = bytes([0x43, 0x00, 0x00, 0x03])
|
||||||
|
PWR_CONTROLLER = bytes([0x43, 0x00, 0x00, 0x04])
|
||||||
|
|
||||||
MISC_TM_STORE = bytes([0x73, 0x02, 0x00, 0x01])
|
MISC_TM_STORE = bytes([0x73, 0x02, 0x00, 0x01])
|
||||||
OK_TM_STORE = bytes([0x73, 0x02, 0x00, 0x02])
|
OK_TM_STORE = bytes([0x73, 0x02, 0x00, 0x02])
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
0x00005060;P60DOCK_TEST_TASK
|
0x00005060;P60DOCK_TEST_TASK
|
||||||
0x43000002;ACS_CONTROLLER
|
0x43000002;ACS_CONTROLLER
|
||||||
0x43000003;CORE_CONTROLLER
|
0x43000003;CORE_CONTROLLER
|
||||||
|
0x43000004;POWER_CONTROLLER
|
||||||
0x43000006;GLOBAL_JSON_CFG
|
0x43000006;GLOBAL_JSON_CFG
|
||||||
0x43400001;THERMAL_CONTROLLER
|
0x43400001;THERMAL_CONTROLLER
|
||||||
0x44120006;MGM_0_LIS3_HANDLER
|
0x44120006;MGM_0_LIS3_HANDLER
|
||||||
@ -161,6 +162,7 @@
|
|||||||
0x73010002;PL_SUBSYSTEM
|
0x73010002;PL_SUBSYSTEM
|
||||||
0x73010003;TCS_SUBSYSTEM
|
0x73010003;TCS_SUBSYSTEM
|
||||||
0x73010004;COM_SUBSYSTEM
|
0x73010004;COM_SUBSYSTEM
|
||||||
|
0x73010005;EPS_SUBSYSTEM
|
||||||
0x73020001;MISC_TM_STORE
|
0x73020001;MISC_TM_STORE
|
||||||
0x73020002;OK_TM_STORE
|
0x73020002;OK_TM_STORE
|
||||||
0x73020003;NOT_OK_TM_STORE
|
0x73020003;NOT_OK_TM_STORE
|
||||||
|
|
@ -210,6 +210,7 @@ Full ID (hex); Name; Description; Unique ID; Subsytem Name; File Path
|
|||||||
0x27a8;DHI_NoReplyExpected;No description;168;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
0x27a8;DHI_NoReplyExpected;No description;168;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
||||||
0x27a9;DHI_NonOpTemperature;No description;169;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
0x27a9;DHI_NonOpTemperature;No description;169;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
||||||
0x27aa;DHI_CommandNotImplemented;No description;170;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
0x27aa;DHI_CommandNotImplemented;No description;170;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
||||||
|
0x27ab;DHI_NonOpStateOfCharge;No description;171;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
||||||
0x27b0;DHI_ChecksumError;No description;176;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
0x27b0;DHI_ChecksumError;No description;176;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
||||||
0x27b1;DHI_LengthMissmatch;No description;177;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
0x27b1;DHI_LengthMissmatch;No description;177;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
||||||
0x27b2;DHI_InvalidData;No description;178;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
0x27b2;DHI_InvalidData;No description;178;DEVICE_HANDLER_IF;fsfw/src/fsfw/devicehandlers/DeviceHandlerIF.h
|
||||||
|
|
163
eive_tmtc/pus_tc/tc_handler.py
Normal file
163
eive_tmtc/pus_tc/tc_handler.py
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
import logging
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
from eive_tmtc.config.definitions import (
|
||||||
|
CFDP_REMOTE_ENTITY_ID,
|
||||||
|
PUS_APID,
|
||||||
|
CFDP_LOCAL_ENTITY_ID,
|
||||||
|
)
|
||||||
|
from eive_tmtc.pus_tc.procedure_packer import handle_default_procedure
|
||||||
|
from tmtccmd import TcHandlerBase, ProcedureWrapper
|
||||||
|
from tmtccmd.cfdp.defs import CfdpRequestType
|
||||||
|
from tmtccmd.cfdp.handler import CfdpInCcsdsHandler
|
||||||
|
from tmtccmd.logging import get_current_time_string
|
||||||
|
from tmtccmd.logging.pus import RawTmtcTimedLogWrapper
|
||||||
|
from tmtccmd.tc import (
|
||||||
|
DefaultPusQueueHelper,
|
||||||
|
QueueWrapper,
|
||||||
|
FeedWrapper,
|
||||||
|
TcProcedureType,
|
||||||
|
SendCbParams,
|
||||||
|
TcQueueEntryType,
|
||||||
|
)
|
||||||
|
from tmtccmd.config.cfdp import generic_cfdp_params_to_put_request
|
||||||
|
from spacepackets.ecss import PusVerificator
|
||||||
|
from tmtccmd.util import FileSeqCountProvider
|
||||||
|
from spacepackets.cfdp import PduHolder, DirectiveType
|
||||||
|
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TcHandler(TcHandlerBase):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
seq_count_provider: FileSeqCountProvider,
|
||||||
|
cfdp_in_ccsds_handler: CfdpInCcsdsHandler,
|
||||||
|
pus_verificator: PusVerificator,
|
||||||
|
high_level_file_logger: logging.Logger,
|
||||||
|
raw_pus_logger: RawTmtcTimedLogWrapper,
|
||||||
|
gui: bool,
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
self.cfdp_handler_started = False
|
||||||
|
self.seq_count_provider = seq_count_provider
|
||||||
|
self.pus_verificator = pus_verificator
|
||||||
|
self.high_level_file_logger = high_level_file_logger
|
||||||
|
self.pus_raw_logger = raw_pus_logger
|
||||||
|
self.gui = gui
|
||||||
|
self.proxy_op = True
|
||||||
|
self.queue_helper = DefaultPusQueueHelper(
|
||||||
|
queue_wrapper=QueueWrapper.empty(),
|
||||||
|
default_pus_apid=PUS_APID,
|
||||||
|
seq_cnt_provider=seq_count_provider,
|
||||||
|
pus_verificator=pus_verificator,
|
||||||
|
tc_sched_timestamp_len=4,
|
||||||
|
)
|
||||||
|
self.cfdp_in_ccsds_handler = cfdp_in_ccsds_handler
|
||||||
|
|
||||||
|
def cfdp_done(self) -> bool:
|
||||||
|
if self.cfdp_handler_started:
|
||||||
|
if (
|
||||||
|
not self.cfdp_in_ccsds_handler.put_request_pending()
|
||||||
|
and not self.proxy_op
|
||||||
|
):
|
||||||
|
self.cfdp_handler_started = False
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
||||||
|
self.queue_helper.queue_wrapper = wrapper.queue_wrapper
|
||||||
|
if info.proc_type == TcProcedureType.DEFAULT:
|
||||||
|
handle_default_procedure(self, info.to_def_procedure(), self.queue_helper)
|
||||||
|
elif info.proc_type == TcProcedureType.CFDP:
|
||||||
|
self.handle_cfdp_procedure(info)
|
||||||
|
|
||||||
|
def send_cb(self, send_params: SendCbParams):
|
||||||
|
entry_helper = send_params.entry
|
||||||
|
if entry_helper.is_tc:
|
||||||
|
if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
|
||||||
|
pus_tc_wrapper = entry_helper.to_pus_tc_entry()
|
||||||
|
# pus_tc_wrapper.pus_tc.apid = PUS_APID
|
||||||
|
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
||||||
|
self.pus_raw_logger.log_tc(pus_tc_wrapper.pus_tc)
|
||||||
|
tc_info_string = f"Sent {pus_tc_wrapper.pus_tc}"
|
||||||
|
_LOGGER.info(tc_info_string)
|
||||||
|
self.high_level_file_logger.info(
|
||||||
|
f"{get_current_time_string(True)}: {tc_info_string}"
|
||||||
|
)
|
||||||
|
# with open("tc.bin", "wb") as of:
|
||||||
|
# of.write(raw_tc)
|
||||||
|
send_params.com_if.send(raw_tc)
|
||||||
|
elif entry_helper.entry_type == TcQueueEntryType.CCSDS_TC:
|
||||||
|
cfdp_packet_in_ccsds = entry_helper.to_space_packet_entry()
|
||||||
|
send_params.com_if.send(cfdp_packet_in_ccsds.space_packet.pack())
|
||||||
|
# TODO: Log raw CFDP packets similarly to how PUS packets are logged.
|
||||||
|
# - Log full raw format including space packet wrapper
|
||||||
|
# - Log context information: Transaction ID, and PDU type and directive
|
||||||
|
# Could re-use file logger. Should probably do that
|
||||||
|
# print(f"sending packet: [{cfdp_packet_in_ccsds.space_packet.pack()}]")
|
||||||
|
# with open(f"cfdp_packet_{self.cfdp_counter}", "wb") as of:
|
||||||
|
# of.write(cfdp_packet_in_ccsds.space_packet.pack())
|
||||||
|
# self.cfdp_counter += 1
|
||||||
|
elif entry_helper.entry_type == TcQueueEntryType.LOG:
|
||||||
|
log_entry = entry_helper.to_log_entry()
|
||||||
|
_LOGGER.info(log_entry.log_str)
|
||||||
|
self.high_level_file_logger.info(log_entry.log_str)
|
||||||
|
|
||||||
|
def handle_cfdp_procedure(self, info: ProcedureWrapper):
|
||||||
|
cfdp_procedure = info.to_cfdp_procedure()
|
||||||
|
if cfdp_procedure.cfdp_request_type == CfdpRequestType.PUT:
|
||||||
|
if (
|
||||||
|
not self.cfdp_in_ccsds_handler.put_request_pending()
|
||||||
|
and not self.cfdp_handler_started
|
||||||
|
):
|
||||||
|
put_req_cfg_wrapper = cfdp_procedure.request_wrapper.to_put_request()
|
||||||
|
if put_req_cfg_wrapper.cfg.proxy_op:
|
||||||
|
self.proxy_op = True
|
||||||
|
put_req = generic_cfdp_params_to_put_request(
|
||||||
|
params=put_req_cfg_wrapper.cfg,
|
||||||
|
local_id=CFDP_LOCAL_ENTITY_ID,
|
||||||
|
remote_id=CFDP_REMOTE_ENTITY_ID,
|
||||||
|
dest_id_proxy_put_req=CFDP_LOCAL_ENTITY_ID,
|
||||||
|
)
|
||||||
|
_LOGGER.info(
|
||||||
|
f"CFDP: Starting file put request with parameters:\n{put_req}"
|
||||||
|
)
|
||||||
|
self.cfdp_in_ccsds_handler.cfdp_handler.put_request(put_req)
|
||||||
|
self.cfdp_handler_started = True
|
||||||
|
|
||||||
|
for source_pair, dest_pair in self.cfdp_in_ccsds_handler:
|
||||||
|
pdu, sp = source_pair
|
||||||
|
pdu = cast(PduHolder, pdu)
|
||||||
|
if pdu.is_file_directive:
|
||||||
|
if pdu.pdu_directive_type == DirectiveType.METADATA_PDU:
|
||||||
|
metadata = pdu.to_metadata_pdu()
|
||||||
|
self.queue_helper.add_log_cmd(
|
||||||
|
"CFDP Source: Sending Metadata PDU for file with size "
|
||||||
|
f"{metadata.file_size}"
|
||||||
|
)
|
||||||
|
elif pdu.pdu_directive_type == DirectiveType.EOF_PDU:
|
||||||
|
self.queue_helper.add_log_cmd(
|
||||||
|
"CFDP Source: Sending EOF PDU"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
fd_pdu = pdu.to_file_data_pdu()
|
||||||
|
self.queue_helper.add_log_cmd(
|
||||||
|
"CFDP Source: Sending File Data PDU for segment at offset "
|
||||||
|
f"{fd_pdu.offset} with length {len(fd_pdu.file_data)}"
|
||||||
|
)
|
||||||
|
self.queue_helper.add_ccsds_tc(sp)
|
||||||
|
self.cfdp_in_ccsds_handler.confirm_source_packet_sent()
|
||||||
|
self.cfdp_in_ccsds_handler.source_handler.state_machine()
|
||||||
|
|
||||||
|
def queue_finished_cb(self, info: ProcedureWrapper):
|
||||||
|
if info is not None:
|
||||||
|
if info.proc_type == TcQueueEntryType.PUS_TC:
|
||||||
|
def_proc = info.to_def_procedure()
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Finished queue for service {def_proc.service} and op code"
|
||||||
|
f" {def_proc.op_code}"
|
||||||
|
)
|
||||||
|
elif info.proc_type == TcProcedureType.CFDP:
|
||||||
|
_LOGGER.info("Finished CFDP queue")
|
@ -242,7 +242,7 @@ def handle_skyview_data(pw: PrintWrapper, hk_data: bytes):
|
|||||||
pw.dlog(f"Skyview Time: {unix} unix-sec")
|
pw.dlog(f"Skyview Time: {unix} unix-sec")
|
||||||
pw.dlog(
|
pw.dlog(
|
||||||
"{:<8} {:<8} {:<8} {:<8} {:<8}".format(
|
"{:<8} {:<8} {:<8} {:<8} {:<8}".format(
|
||||||
"PRN_ID", "AZ [°]", "EL [°]", "S2N [dBW]", "USED"
|
"PRN_ID", "AZ [°]", "EL [°]", "S2N [dBHz]", "USED"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for idx in range(GpsInfo.MAX_SATELLITES):
|
for idx in range(GpsInfo.MAX_SATELLITES):
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
from .subsystem import add_eps_subsystem_cmds
|
||||||
|
302
eive_tmtc/tmtc/power/pwr_ctrl.py
Normal file
302
eive_tmtc/tmtc/power/pwr_ctrl.py
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
import datetime
|
||||||
|
import enum
|
||||||
|
import logging
|
||||||
|
import struct
|
||||||
|
|
||||||
|
from eive_tmtc.config.definitions import CustomServiceList
|
||||||
|
from eive_tmtc.config.object_ids import PWR_CONTROLLER
|
||||||
|
from eive_tmtc.pus_tm.defs import PrintWrapper
|
||||||
|
from tmtccmd.config.tmtc import (
|
||||||
|
tmtc_definitions_provider,
|
||||||
|
TmtcDefinitionWrapper,
|
||||||
|
OpCodeEntry,
|
||||||
|
)
|
||||||
|
from tmtccmd.tc import service_provider
|
||||||
|
from tmtccmd.tc.queue import DefaultPusQueueHelper
|
||||||
|
from tmtccmd.tc.pus_200_fsfw_mode import Mode, pack_mode_command
|
||||||
|
from tmtccmd.tc.decorator import ServiceProviderParams
|
||||||
|
from tmtccmd.tc.pus_3_fsfw_hk import (
|
||||||
|
generate_one_hk_command,
|
||||||
|
make_sid,
|
||||||
|
enable_periodic_hk_command_with_interval,
|
||||||
|
disable_periodic_hk_command,
|
||||||
|
)
|
||||||
|
from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter
|
||||||
|
|
||||||
|
from tmtccmd.tc.pus_20_fsfw_param import create_load_param_cmd
|
||||||
|
|
||||||
|
from tmtccmd.pus.s20_fsfw_param_defs import (
|
||||||
|
create_scalar_float_parameter,
|
||||||
|
create_scalar_double_parameter,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SetId(enum.IntEnum):
|
||||||
|
CORE_HK_SET = 0
|
||||||
|
ENABLE_PL_SET = 1
|
||||||
|
|
||||||
|
|
||||||
|
# class ActionId(enum.IntEnum):
|
||||||
|
|
||||||
|
|
||||||
|
class ParamId(enum.IntEnum):
|
||||||
|
BATTERY_INTERNAL_RESISTANCE = 0
|
||||||
|
BATTERY_MAXIMUM_CAPACITY = 1
|
||||||
|
COULOMB_COUNTER_VOLTAGE_UPPER_THRESHOLD = 2
|
||||||
|
MAX_ALLOWED_TIME_DIFF = 3
|
||||||
|
PAYLOAD_OP_LIMIT_ON = 4
|
||||||
|
PAYLOAD_OP_LIMIT_LOW = 5
|
||||||
|
HIGHER_MODES_LIMIT = 6
|
||||||
|
|
||||||
|
|
||||||
|
class OpCodes:
|
||||||
|
OFF = ["mode_off"]
|
||||||
|
ON = ["mode_on"]
|
||||||
|
NML = ["mode_normal"]
|
||||||
|
SET_PARAMETER = ["set_parameter"]
|
||||||
|
REQUEST_CORE_HK = ["core_hk"]
|
||||||
|
ENABLE_CORE_HK = ["core_enable_hk"]
|
||||||
|
DISABLE_CORE_HK = ["core_disable_hk"]
|
||||||
|
REQUEST_ENABLE_PL_HK = ["enable_pl_hk"]
|
||||||
|
ENABLE_ENABLE_PL_HK = ["enable_pl_enable_hk"]
|
||||||
|
DISABLE_ENABLE_PL_HK = ["enable_pl_disable_hk"]
|
||||||
|
|
||||||
|
|
||||||
|
class Info:
|
||||||
|
OFF = "PWR Ctrl Mode to OFF"
|
||||||
|
ON = "PWR Ctrl Mode to ON"
|
||||||
|
NML = "PWR Ctrl Mode to NORMAL"
|
||||||
|
SET_PARAMETER = "Set Parameter"
|
||||||
|
REQUEST_CORE_HK = "Request Core HK once"
|
||||||
|
ENABLE_CORE_HK = "Enable Core HK Data Generation"
|
||||||
|
DISABLE_CORE_HK = "Disable Core HK Data Generation"
|
||||||
|
REQUEST_ENABLE_PL_HK = "Request Enable PL HK once"
|
||||||
|
ENABLE_ENABLE_PL_HK = "Enable Enable PL HK Data Generation"
|
||||||
|
DISABLE_ENABLE_PL_HK = "Disable Enable PL HK Data Generation"
|
||||||
|
|
||||||
|
|
||||||
|
@tmtc_definitions_provider
|
||||||
|
def acs_cmd_defs(defs: TmtcDefinitionWrapper):
|
||||||
|
oce = OpCodeEntry()
|
||||||
|
oce.add(keys=OpCodes.OFF, info=Info.OFF)
|
||||||
|
oce.add(keys=OpCodes.ON, info=Info.ON)
|
||||||
|
oce.add(keys=OpCodes.NML, info=Info.NML)
|
||||||
|
oce.add(keys=OpCodes.SET_PARAMETER, info=Info.SET_PARAMETER)
|
||||||
|
oce.add(keys=OpCodes.REQUEST_CORE_HK, info=Info.REQUEST_CORE_HK)
|
||||||
|
oce.add(keys=OpCodes.ENABLE_CORE_HK, info=Info.ENABLE_CORE_HK)
|
||||||
|
oce.add(keys=OpCodes.DISABLE_CORE_HK, info=Info.DISABLE_CORE_HK)
|
||||||
|
oce.add(keys=OpCodes.REQUEST_ENABLE_PL_HK, info=Info.REQUEST_ENABLE_PL_HK)
|
||||||
|
oce.add(keys=OpCodes.ENABLE_ENABLE_PL_HK, info=Info.ENABLE_ENABLE_PL_HK)
|
||||||
|
oce.add(keys=OpCodes.DISABLE_ENABLE_PL_HK, info=Info.DISABLE_ENABLE_PL_HK)
|
||||||
|
defs.add_service(
|
||||||
|
name=CustomServiceList.PWR_CTRL.value, info="PWR Controller", op_code_entry=oce
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@service_provider(CustomServiceList.PWR_CTRL.value)
|
||||||
|
def pack_acs_ctrl_command(p: ServiceProviderParams):
|
||||||
|
op_code = p.op_code
|
||||||
|
q = p.queue_helper
|
||||||
|
if op_code in OpCodes.OFF:
|
||||||
|
q.add_log_cmd(f"{Info.OFF}")
|
||||||
|
q.add_pus_tc(pack_mode_command(PWR_CONTROLLER, Mode.OFF, 0))
|
||||||
|
elif op_code in OpCodes.ON:
|
||||||
|
q.add_log_cmd(f"{Info.ON}")
|
||||||
|
q.add_pus_tc(pack_mode_command(PWR_CONTROLLER, Mode.ON, 0))
|
||||||
|
elif op_code in OpCodes.NML:
|
||||||
|
q.add_log_cmd(f"{Info.NML}")
|
||||||
|
q.add_pus_tc(pack_mode_command(PWR_CONTROLLER, Mode.NORMAL, 0))
|
||||||
|
elif op_code in OpCodes.SET_PARAMETER:
|
||||||
|
q.add_log_cmd(f"{Info.SET_PARAMETER}")
|
||||||
|
set_pwr_ctrl_param(q)
|
||||||
|
elif op_code in OpCodes.REQUEST_CORE_HK:
|
||||||
|
q.add_log_cmd(Info.REQUEST_CORE_HK)
|
||||||
|
q.add_pus_tc(
|
||||||
|
generate_one_hk_command(make_sid(PWR_CONTROLLER, SetId.CORE_HK_SET))
|
||||||
|
)
|
||||||
|
elif op_code in OpCodes.ENABLE_CORE_HK:
|
||||||
|
interval = float(input("Please specify interval in floating point seconds: "))
|
||||||
|
q.add_log_cmd(Info.ENABLE_CORE_HK)
|
||||||
|
cmd_tuple = enable_periodic_hk_command_with_interval(
|
||||||
|
False, make_sid(PWR_CONTROLLER, SetId.CORE_HK_SET), interval
|
||||||
|
)
|
||||||
|
q.add_pus_tc(cmd_tuple[0])
|
||||||
|
q.add_pus_tc(cmd_tuple[1])
|
||||||
|
elif op_code in OpCodes.DISABLE_CORE_HK:
|
||||||
|
q.add_log_cmd(Info.DISABLE_CORE_HK)
|
||||||
|
q.add_pus_tc(
|
||||||
|
disable_periodic_hk_command(
|
||||||
|
False, make_sid(PWR_CONTROLLER, SetId.CORE_HK_SET)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif op_code in OpCodes.REQUEST_ENABLE_PL_HK:
|
||||||
|
q.add_log_cmd(Info.REQUEST_ENABLE_PL_HK)
|
||||||
|
q.add_pus_tc(
|
||||||
|
generate_one_hk_command(make_sid(PWR_CONTROLLER, SetId.ENABLE_PL_SET))
|
||||||
|
)
|
||||||
|
elif op_code in OpCodes.ENABLE_ENABLE_PL_HK:
|
||||||
|
interval = float(input("Please specify interval in floating point seconds: "))
|
||||||
|
q.add_log_cmd(Info.ENABLE_ENABLE_PL_HK)
|
||||||
|
cmd_tuple = enable_periodic_hk_command_with_interval(
|
||||||
|
False, make_sid(PWR_CONTROLLER, SetId.ENABLE_PL_SET), interval
|
||||||
|
)
|
||||||
|
q.add_pus_tc(cmd_tuple[0])
|
||||||
|
q.add_pus_tc(cmd_tuple[1])
|
||||||
|
elif op_code in OpCodes.DISABLE_ENABLE_PL_HK:
|
||||||
|
q.add_log_cmd(Info.DISABLE_ENABLE_PL_HK)
|
||||||
|
q.add_pus_tc(
|
||||||
|
disable_periodic_hk_command(
|
||||||
|
False, make_sid(PWR_CONTROLLER, SetId.ENABLE_PL_SET)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def set_pwr_ctrl_param(q: DefaultPusQueueHelper):
|
||||||
|
for val in ParamId:
|
||||||
|
print("{:<2}: {:<20}".format(val, val.name))
|
||||||
|
param = int(input("Specify parameter to set \n" ""))
|
||||||
|
match param:
|
||||||
|
case ParamId.BATTERY_INTERNAL_RESISTANCE:
|
||||||
|
value = float(input("Specify parameter value to set [Ohm]: "))
|
||||||
|
q.add_pus_tc(
|
||||||
|
create_load_param_cmd(
|
||||||
|
create_scalar_float_parameter(
|
||||||
|
object_id=PWR_CONTROLLER,
|
||||||
|
domain_id=0,
|
||||||
|
unique_id=ParamId.BATTERY_INTERNAL_RESISTANCE,
|
||||||
|
parameter=value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case ParamId.BATTERY_MAXIMUM_CAPACITY:
|
||||||
|
value = float(input("Specify parameter value to set [Ah]: "))
|
||||||
|
q.add_pus_tc(
|
||||||
|
create_load_param_cmd(
|
||||||
|
create_scalar_float_parameter(
|
||||||
|
object_id=PWR_CONTROLLER,
|
||||||
|
domain_id=0,
|
||||||
|
unique_id=ParamId.BATTERY_MAXIMUM_CAPACITY,
|
||||||
|
parameter=value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case ParamId.COULOMB_COUNTER_VOLTAGE_UPPER_THRESHOLD:
|
||||||
|
value = float(input("Specify parameter value to set [V]: "))
|
||||||
|
q.add_pus_tc(
|
||||||
|
create_load_param_cmd(
|
||||||
|
create_scalar_float_parameter(
|
||||||
|
object_id=PWR_CONTROLLER,
|
||||||
|
domain_id=0,
|
||||||
|
unique_id=ParamId.COULOMB_COUNTER_VOLTAGE_UPPER_THRESHOLD,
|
||||||
|
parameter=value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case ParamId.MAX_ALLOWED_TIME_DIFF:
|
||||||
|
value = float(input("Specify parameter value to set [s]: "))
|
||||||
|
q.add_pus_tc(
|
||||||
|
create_load_param_cmd(
|
||||||
|
create_scalar_double_parameter(
|
||||||
|
object_id=PWR_CONTROLLER,
|
||||||
|
domain_id=0,
|
||||||
|
unique_id=ParamId.MAX_ALLOWED_TIME_DIFF,
|
||||||
|
parameter=value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case ParamId.PAYLOAD_OP_LIMIT_ON:
|
||||||
|
value = float(input("Specify parameter value to set [1]: "))
|
||||||
|
q.add_pus_tc(
|
||||||
|
create_load_param_cmd(
|
||||||
|
create_scalar_float_parameter(
|
||||||
|
object_id=PWR_CONTROLLER,
|
||||||
|
domain_id=0,
|
||||||
|
unique_id=ParamId.PAYLOAD_OP_LIMIT_ON,
|
||||||
|
parameter=value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case ParamId.PAYLOAD_OP_LIMIT_LOW:
|
||||||
|
value = float(input("Specify parameter value to set [1]: "))
|
||||||
|
q.add_pus_tc(
|
||||||
|
create_load_param_cmd(
|
||||||
|
create_scalar_float_parameter(
|
||||||
|
object_id=PWR_CONTROLLER,
|
||||||
|
domain_id=0,
|
||||||
|
unique_id=ParamId.PAYLOAD_OP_LIMIT_LOW,
|
||||||
|
parameter=value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case ParamId.HIGHER_MODES_LIMIT:
|
||||||
|
value = float(input("Specify parameter value to set [1]: "))
|
||||||
|
q.add_pus_tc(
|
||||||
|
create_load_param_cmd(
|
||||||
|
create_scalar_float_parameter(
|
||||||
|
object_id=PWR_CONTROLLER,
|
||||||
|
domain_id=0,
|
||||||
|
unique_id=ParamId.HIGHER_MODES_LIMIT,
|
||||||
|
parameter=value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_pwr_ctrl_hk_data(
|
||||||
|
pw: PrintWrapper,
|
||||||
|
set_id: int,
|
||||||
|
hk_data: bytes,
|
||||||
|
packet_time: datetime.datetime,
|
||||||
|
):
|
||||||
|
pw.ilog(_LOGGER, f"Received PWR CTRL HK with packet time {packet_time}")
|
||||||
|
match set_id:
|
||||||
|
case SetId.CORE_HK_SET:
|
||||||
|
handle_core_hk_data(pw, hk_data)
|
||||||
|
case SetId.ENABLE_PL_SET:
|
||||||
|
handle_enable_pl_data(pw, hk_data)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_core_hk_data(pw: PrintWrapper, hk_data: bytes):
|
||||||
|
pw.dlog("Received Core HK Set")
|
||||||
|
fmt_int16 = "!h"
|
||||||
|
fmt_float = "!f"
|
||||||
|
inc_len_int16 = struct.calcsize(fmt_int16)
|
||||||
|
inc_len_float = struct.calcsize(fmt_float)
|
||||||
|
if len(hk_data) < inc_len_int16 + 2 * inc_len_float:
|
||||||
|
pw.dlog("Received HK set too small")
|
||||||
|
return
|
||||||
|
current_idx = 0
|
||||||
|
total_battery_current = struct.unpack(
|
||||||
|
fmt_int16, hk_data[current_idx : current_idx + inc_len_int16]
|
||||||
|
)[0]
|
||||||
|
current_idx += inc_len_int16
|
||||||
|
open_circuit_voltage_charge = struct.unpack(
|
||||||
|
fmt_float, hk_data[current_idx : current_idx + inc_len_float]
|
||||||
|
)[0]
|
||||||
|
current_idx += inc_len_float
|
||||||
|
coulomb_counter_charge = struct.unpack(
|
||||||
|
fmt_float, hk_data[current_idx : current_idx + inc_len_float]
|
||||||
|
)[0]
|
||||||
|
current_idx += inc_len_float
|
||||||
|
pw.dlog(f"Total Battery Current: {total_battery_current} [mA]")
|
||||||
|
pw.dlog(f"Open Circuit Voltage Charge: {open_circuit_voltage_charge*100:8.3f} [%]")
|
||||||
|
pw.dlog(f"Coulomb Counter Charge: {coulomb_counter_charge*100:8.3f} [%]")
|
||||||
|
FsfwTmTcPrinter.get_validity_buffer(hk_data[current_idx:], num_vars=3)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_enable_pl_data(pw: PrintWrapper, hk_data: bytes):
|
||||||
|
pw.dlog("Received Enable PL HK Set")
|
||||||
|
fmt_uint16 = "!B"
|
||||||
|
inc_len_uint16 = struct.calcsize(fmt_uint16)
|
||||||
|
if len(hk_data) < inc_len_uint16:
|
||||||
|
pw.dlog("Received HK set too small")
|
||||||
|
return
|
||||||
|
current_idx = 0
|
||||||
|
pl_use_allowed = struct.unpack(
|
||||||
|
fmt_uint16, hk_data[current_idx : current_idx + inc_len_uint16]
|
||||||
|
)[0]
|
||||||
|
current_idx += inc_len_uint16
|
||||||
|
pw.dlog(f"PL Use Allowed: {pl_use_allowed}")
|
||||||
|
FsfwTmTcPrinter.get_validity_buffer(hk_data[current_idx:], num_vars=1)
|
68
eive_tmtc/tmtc/power/subsystem.py
Normal file
68
eive_tmtc/tmtc/power/subsystem.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import enum
|
||||||
|
from typing import Tuple, Dict
|
||||||
|
|
||||||
|
from spacepackets.ecss import PusTelecommand
|
||||||
|
from eive_tmtc.tmtc.common import pack_mode_cmd_with_info
|
||||||
|
from eive_tmtc.config.object_ids import EPS_SUBSYSTEM_ID
|
||||||
|
from eive_tmtc.config.definitions import CustomServiceList
|
||||||
|
from tmtccmd.config.tmtc import (
|
||||||
|
tmtc_definitions_provider,
|
||||||
|
TmtcDefinitionWrapper,
|
||||||
|
OpCodeEntry,
|
||||||
|
)
|
||||||
|
from tmtccmd.tc.pus_200_fsfw_mode import Subservice as ModeSubservices, Mode
|
||||||
|
from tmtccmd.tc import service_provider
|
||||||
|
from tmtccmd.tc.decorator import ServiceProviderParams
|
||||||
|
|
||||||
|
|
||||||
|
class OpCode(str, enum.Enum):
|
||||||
|
OFF = "off"
|
||||||
|
NML = "normal"
|
||||||
|
REPORT_ALL_MODES = "all_modes"
|
||||||
|
|
||||||
|
|
||||||
|
class Info(str, enum.Enum):
|
||||||
|
OFF = "Off Mode Command"
|
||||||
|
NML = "Normal Mode Command"
|
||||||
|
REPORT_ALL_MODES = "Report All Modes Recursively"
|
||||||
|
|
||||||
|
|
||||||
|
HANDLER_LIST: Dict[str, Tuple[int, int, str]] = {
|
||||||
|
OpCode.OFF: (Mode.OFF, 0, Info.OFF),
|
||||||
|
OpCode.NML: (Mode.NORMAL, 0, Info.NML),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@service_provider(CustomServiceList.EPS_SS.value)
|
||||||
|
def build_eps_subsystem_cmd(p: ServiceProviderParams):
|
||||||
|
op_code = p.op_code
|
||||||
|
q = p.queue_helper
|
||||||
|
info_prefix = "EPS Subsystem"
|
||||||
|
if op_code in OpCode.REPORT_ALL_MODES:
|
||||||
|
q.add_log_cmd(f"{info_prefix}: {Info.REPORT_ALL_MODES}")
|
||||||
|
q.add_pus_tc(
|
||||||
|
PusTelecommand(
|
||||||
|
service=200,
|
||||||
|
subservice=ModeSubservices.TC_MODE_ANNOUNCE_RECURSIVE,
|
||||||
|
app_data=EPS_SUBSYSTEM_ID,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
mode_info_tup = HANDLER_LIST.get(op_code)
|
||||||
|
if mode_info_tup is None:
|
||||||
|
return
|
||||||
|
pack_mode_cmd_with_info(
|
||||||
|
object_id=EPS_SUBSYSTEM_ID,
|
||||||
|
info=f"{info_prefix}: {mode_info_tup[2]}",
|
||||||
|
mode=mode_info_tup[0],
|
||||||
|
submode=mode_info_tup[1],
|
||||||
|
q=q,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@tmtc_definitions_provider
|
||||||
|
def add_eps_subsystem_cmds(defs: TmtcDefinitionWrapper):
|
||||||
|
oce = OpCodeEntry()
|
||||||
|
for op_code, (_, _, info) in HANDLER_LIST.items():
|
||||||
|
oce.add(op_code, info)
|
||||||
|
oce.add(OpCode.REPORT_ALL_MODES, Info.REPORT_ALL_MODES)
|
||||||
|
defs.add_service(CustomServiceList.EPS_SS, "EPS Subsystem", oce)
|
42
lint.py
42
lint.py
@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
exclude_dirs_flag = ""
|
|
||||||
if not os.path.exists("setup.cfg"):
|
|
||||||
exclude_dirs_flag = (
|
|
||||||
"--exclude .git,__pycache__,docs/conf.py,old,build,dist,venv"
|
|
||||||
)
|
|
||||||
additional_flags_both_steps = "--count --statistics"
|
|
||||||
additional_flags_first_step = "--select=E9,F63,F7,F82 --show-source"
|
|
||||||
python_exe = ""
|
|
||||||
if os.name == "nt":
|
|
||||||
python_exe = "py -m"
|
|
||||||
flake8_first_step_cmd = (
|
|
||||||
f"{python_exe} flake8 . {additional_flags_both_steps} "
|
|
||||||
f"{additional_flags_first_step} {exclude_dirs_flag}"
|
|
||||||
)
|
|
||||||
status = os.system(flake8_first_step_cmd)
|
|
||||||
if os.name == "nt":
|
|
||||||
if status != 0:
|
|
||||||
print(f"Flake8 linter errors with status {status}")
|
|
||||||
else:
|
|
||||||
if os.WEXITSTATUS(status) != 0:
|
|
||||||
print(f"Flake8 linter errors with status {status}")
|
|
||||||
sys.exit(0)
|
|
||||||
additional_flags_second_step = (
|
|
||||||
'--exit-zero --max-complexity=10 --per-file-ignores="__init__.py:F401"'
|
|
||||||
)
|
|
||||||
if not os.path.exists("setup.cfg"):
|
|
||||||
additional_flags_second_step += " --max-line-length=100"
|
|
||||||
flake8_second_step_cmd = (
|
|
||||||
f"{python_exe} flake8 . {additional_flags_both_steps} "
|
|
||||||
f" {additional_flags_second_step} {exclude_dirs_flag}"
|
|
||||||
)
|
|
||||||
os.system(flake8_second_step_cmd)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
name = "eive-tmtc"
|
name = "eive-tmtc"
|
||||||
description = "TMTC Commander EIVE"
|
description = "TMTC Commander EIVE"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "5.5.1"
|
version = "5.7.0"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
authors = [
|
authors = [
|
||||||
@ -29,8 +29,9 @@ classifiers = [
|
|||||||
"Topic :: Scientific/Engineering"
|
"Topic :: Scientific/Engineering"
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tmtccmd == 6.0.0rc0",
|
"tmtccmd ~= 6.0",
|
||||||
"python-dateutil ~= 2.8",
|
"python-dateutil ~= 2.8",
|
||||||
|
# "tmtccmd @ git+https://github.com/robamu-org/tmtccmd@main"
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
@ -42,3 +43,8 @@ include-package-data = true
|
|||||||
# Auto-Discovery is problematic for some reason, so use custom-discovery
|
# Auto-Discovery is problematic for some reason, so use custom-discovery
|
||||||
[tool.setuptools.packages]
|
[tool.setuptools.packages]
|
||||||
find = {}
|
find = {}
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
ignore = ["E501"]
|
||||||
|
[tool.ruff.extend-per-file-ignores]
|
||||||
|
"__init__.py" = ["F401"]
|
||||||
|
@ -7,7 +7,7 @@ Checklist for new releases
|
|||||||
2. Update `CHANGELOG.md`: Convert `unreleased` section into version section
|
2. Update `CHANGELOG.md`: Convert `unreleased` section into version section
|
||||||
with date and new `unreleased`section.
|
with date and new `unreleased`section.
|
||||||
3. Run auto-formatter with `black .`
|
3. Run auto-formatter with `black .`
|
||||||
4. Run linter with `flake8 .`
|
4. Run linter with `ruff .`
|
||||||
|
|
||||||
# Post-Release
|
# Post-Release
|
||||||
|
|
||||||
|
12
setup.py
12
setup.py
@ -1,12 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
We do the package handling in the static setup.cfg but include an empty setup.py
|
|
||||||
to allow editable installs https://packaging.python.org/tutorials/packaging-projects/
|
|
||||||
and provide extensibility
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from setuptools import setup
|
|
||||||
except ImportError:
|
|
||||||
from distutils.core import setup
|
|
||||||
|
|
||||||
setup()
|
|
271
tmtcc.py
271
tmtcc.py
@ -3,39 +3,27 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import cast
|
|
||||||
|
|
||||||
from spacepackets.ecss import PusVerificator
|
|
||||||
from spacepackets.version import get_version as get_sp_version
|
|
||||||
from spacepackets.ccsds import SPACE_PACKET_HEADER_SIZE
|
|
||||||
from spacepackets.cfdp import (
|
|
||||||
ConditionCode,
|
|
||||||
ChecksumType,
|
|
||||||
TransmissionMode,
|
|
||||||
PduHolder,
|
|
||||||
DirectiveType,
|
|
||||||
PduFactory,
|
|
||||||
PduType,
|
|
||||||
)
|
|
||||||
|
|
||||||
import tmtccmd
|
import tmtccmd
|
||||||
|
from eive_tmtc.cfdp.fault_handler import EiveCfdpFaultHandler
|
||||||
|
from eive_tmtc.cfdp.tm import CfdpInCcsdsWrapper
|
||||||
|
from eive_tmtc.cfdp.user import EiveCfdpUser
|
||||||
|
from spacepackets.ecss import PusVerificator
|
||||||
|
from spacepackets.version import get_version as get_sp_version
|
||||||
|
from spacepackets.cfdp import (
|
||||||
|
ChecksumType,
|
||||||
|
TransmissionMode,
|
||||||
|
)
|
||||||
|
|
||||||
|
from eive_tmtc.pus_tc.tc_handler import TcHandler
|
||||||
from tmtccmd.logging import add_colorlog_console_logger
|
from tmtccmd.logging import add_colorlog_console_logger
|
||||||
from tmtccmd.cfdp import CfdpUserBase, TransactionId
|
|
||||||
from tmtccmd.cfdp.defs import CfdpRequestType
|
|
||||||
from tmtccmd.cfdp.handler import CfdpInCcsdsHandler
|
from tmtccmd.cfdp.handler import CfdpInCcsdsHandler
|
||||||
from tmtccmd.cfdp.mib import (
|
from tmtccmd.cfdp.mib import (
|
||||||
DefaultFaultHandlerBase,
|
|
||||||
LocalEntityCfg,
|
LocalEntityCfg,
|
||||||
IndicationCfg,
|
IndicationCfg,
|
||||||
RemoteEntityCfg,
|
RemoteEntityCfg,
|
||||||
)
|
)
|
||||||
from tmtccmd.cfdp.user import (
|
from tmtccmd import BackendBase
|
||||||
TransactionFinishedParams,
|
|
||||||
MetadataRecvParams,
|
|
||||||
FileSegmentRecvdParams,
|
|
||||||
)
|
|
||||||
from tmtccmd.tc.handler import SendCbParams
|
|
||||||
from tmtccmd import TcHandlerBase, BackendBase
|
|
||||||
from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider
|
from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider
|
||||||
from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter
|
from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter
|
||||||
|
|
||||||
@ -47,15 +35,6 @@ from tmtccmd.logging.pus import (
|
|||||||
from tmtccmd.pus import VerificationWrapper
|
from tmtccmd.pus import VerificationWrapper
|
||||||
from tmtccmd.tm import SpecificApidHandlerBase, GenericApidHandlerBase, CcsdsTmHandler
|
from tmtccmd.tm import SpecificApidHandlerBase, GenericApidHandlerBase, CcsdsTmHandler
|
||||||
from tmtccmd.core import BackendRequest
|
from tmtccmd.core import BackendRequest
|
||||||
from tmtccmd.logging import get_current_time_string
|
|
||||||
from tmtccmd.tc import (
|
|
||||||
ProcedureWrapper,
|
|
||||||
FeedWrapper,
|
|
||||||
TcProcedureType,
|
|
||||||
TcQueueEntryType,
|
|
||||||
DefaultPusQueueHelper,
|
|
||||||
QueueWrapper,
|
|
||||||
)
|
|
||||||
from tmtccmd.config import (
|
from tmtccmd.config import (
|
||||||
default_json_path,
|
default_json_path,
|
||||||
SetupWrapper,
|
SetupWrapper,
|
||||||
@ -76,7 +55,6 @@ from eive_tmtc.config.definitions import (
|
|||||||
)
|
)
|
||||||
from eive_tmtc.config.hook import EiveHookObject
|
from eive_tmtc.config.hook import EiveHookObject
|
||||||
from eive_tmtc.pus_tm.pus_demux import pus_factory_hook
|
from eive_tmtc.pus_tm.pus_demux import pus_factory_hook
|
||||||
from eive_tmtc.pus_tc.procedure_packer import handle_default_procedure
|
|
||||||
|
|
||||||
_LOGGER = APP_LOGGER
|
_LOGGER = APP_LOGGER
|
||||||
_LOG_LEVEL = logging.INFO
|
_LOG_LEVEL = logging.INFO
|
||||||
@ -86,61 +64,6 @@ ROTATING_TIMED_LOGGER_INTERVAL_WHEN = TimedLogWhen.PER_MINUTE
|
|||||||
ROTATING_TIMED_LOGGER_INTERVAL = 30
|
ROTATING_TIMED_LOGGER_INTERVAL = 30
|
||||||
|
|
||||||
|
|
||||||
class EiveCfdpFaultHandler(DefaultFaultHandlerBase):
|
|
||||||
def notice_of_suspension_cb(self, cond: ConditionCode):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def notice_of_cancellation_cb(self, cond: ConditionCode):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def abandoned_cb(self, cond: ConditionCode):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def ignore_cb(self, cond: ConditionCode):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class EiveCfdpUser(CfdpUserBase):
|
|
||||||
def transaction_indication(self, transaction_id: TransactionId):
|
|
||||||
_LOGGER.info(f"CFDP User: Start of File {transaction_id}")
|
|
||||||
|
|
||||||
def eof_sent_indication(self, transaction_id: TransactionId):
|
|
||||||
_LOGGER.info(f"CFDP User: EOF sent for {transaction_id}")
|
|
||||||
|
|
||||||
def transaction_finished_indication(self, params: TransactionFinishedParams):
|
|
||||||
_LOGGER.info(f"CFDP User: {params.transaction_id} finished")
|
|
||||||
|
|
||||||
def metadata_recv_indication(self, params: MetadataRecvParams):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def file_segment_recv_indication(self, params: FileSegmentRecvdParams):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def report_indication(self, transaction_id: TransactionId, status_report: any):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def suspended_indication(
|
|
||||||
self, transaction_id: TransactionId, cond_code: ConditionCode
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def resumed_indication(self, transaction_id: TransactionId, progress: int):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def fault_indication(
|
|
||||||
self, transaction_id: TransactionId, cond_code: ConditionCode, progress: int
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def abandoned_indication(
|
|
||||||
self, transaction_id: TransactionId, cond_code: ConditionCode, progress: int
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def eof_recv_indication(self, transaction_id: TransactionId):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PusHandler(SpecificApidHandlerBase):
|
class PusHandler(SpecificApidHandlerBase):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -175,153 +98,6 @@ class CustomCcsdsTmHandler(CcsdsTmHandler):
|
|||||||
_LOGGER.debug(f"Received packet {packet.hex(sep=',')} with APID {apid}")
|
_LOGGER.debug(f"Received packet {packet.hex(sep=',')} with APID {apid}")
|
||||||
|
|
||||||
|
|
||||||
class CfdpInCcsdsWrapper(SpecificApidHandlerBase):
|
|
||||||
def __init__(self, cfdp_in_ccsds_handler: CfdpInCcsdsHandler):
|
|
||||||
super().__init__(CFDP_APID, None)
|
|
||||||
self.handler = cfdp_in_ccsds_handler
|
|
||||||
|
|
||||||
def handle_tm(self, packet: bytes, _user_args: any):
|
|
||||||
# Ignore the space packet header. Its only purpose is to use the same protocol and
|
|
||||||
# have a seaprate APID for space packets. If this function is called, the APID is correct.
|
|
||||||
pdu = packet[SPACE_PACKET_HEADER_SIZE:]
|
|
||||||
pdu_base = PduFactory.from_raw(pdu)
|
|
||||||
if pdu_base.pdu_type == PduType.FILE_DATA:
|
|
||||||
_LOGGER.info("Received File Data PDU TM")
|
|
||||||
else:
|
|
||||||
if pdu_base.directive_type == DirectiveType.FINISHED_PDU:
|
|
||||||
_LOGGER.info("Received Finished PDU TM")
|
|
||||||
else:
|
|
||||||
_LOGGER.info(
|
|
||||||
"Received File Directive PDU with type"
|
|
||||||
f" {pdu_base.directive_type!r} TM"
|
|
||||||
)
|
|
||||||
self.handler.pass_pdu_packet(pdu_base)
|
|
||||||
|
|
||||||
|
|
||||||
class TcHandler(TcHandlerBase):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
seq_count_provider: FileSeqCountProvider,
|
|
||||||
cfdp_in_ccsds_wrapper: CfdpInCcsdsWrapper,
|
|
||||||
pus_verificator: PusVerificator,
|
|
||||||
high_level_file_logger: logging.Logger,
|
|
||||||
raw_pus_logger: RawTmtcTimedLogWrapper,
|
|
||||||
gui: bool,
|
|
||||||
):
|
|
||||||
super().__init__()
|
|
||||||
self.cfdp_handler_started = False
|
|
||||||
self.cfdp_dest_id = CFDP_REMOTE_ENTITY_ID
|
|
||||||
self.seq_count_provider = seq_count_provider
|
|
||||||
self.pus_verificator = pus_verificator
|
|
||||||
self.high_level_file_logger = high_level_file_logger
|
|
||||||
self.pus_raw_logger = raw_pus_logger
|
|
||||||
self.gui = gui
|
|
||||||
self.queue_helper = DefaultPusQueueHelper(
|
|
||||||
queue_wrapper=QueueWrapper.empty(),
|
|
||||||
default_pus_apid=PUS_APID,
|
|
||||||
seq_cnt_provider=seq_count_provider,
|
|
||||||
pus_verificator=pus_verificator,
|
|
||||||
tc_sched_timestamp_len=4,
|
|
||||||
)
|
|
||||||
self.cfdp_in_ccsds_wrapper = cfdp_in_ccsds_wrapper
|
|
||||||
|
|
||||||
def cfdp_done(self) -> bool:
|
|
||||||
if self.cfdp_handler_started:
|
|
||||||
if not self.cfdp_in_ccsds_wrapper.handler.put_request_pending():
|
|
||||||
self.cfdp_handler_started = False
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def feed_cb(self, info: ProcedureWrapper, wrapper: FeedWrapper):
|
|
||||||
self.queue_helper.queue_wrapper = wrapper.queue_wrapper
|
|
||||||
if info.proc_type == TcProcedureType.DEFAULT:
|
|
||||||
handle_default_procedure(self, info.to_def_procedure(), self.queue_helper)
|
|
||||||
elif info.proc_type == TcProcedureType.CFDP:
|
|
||||||
self.handle_cfdp_procedure(info)
|
|
||||||
|
|
||||||
def send_cb(self, send_params: SendCbParams):
|
|
||||||
entry_helper = send_params.entry
|
|
||||||
if entry_helper.is_tc:
|
|
||||||
if entry_helper.entry_type == TcQueueEntryType.PUS_TC:
|
|
||||||
pus_tc_wrapper = entry_helper.to_pus_tc_entry()
|
|
||||||
# pus_tc_wrapper.pus_tc.apid = PUS_APID
|
|
||||||
raw_tc = pus_tc_wrapper.pus_tc.pack()
|
|
||||||
self.pus_raw_logger.log_tc(pus_tc_wrapper.pus_tc)
|
|
||||||
tc_info_string = f"Sent {pus_tc_wrapper.pus_tc}"
|
|
||||||
_LOGGER.info(tc_info_string)
|
|
||||||
self.high_level_file_logger.info(
|
|
||||||
f"{get_current_time_string(True)}: {tc_info_string}"
|
|
||||||
)
|
|
||||||
# with open("tc.bin", "wb") as of:
|
|
||||||
# of.write(raw_tc)
|
|
||||||
send_params.com_if.send(raw_tc)
|
|
||||||
elif entry_helper.entry_type == TcQueueEntryType.CCSDS_TC:
|
|
||||||
cfdp_packet_in_ccsds = entry_helper.to_space_packet_entry()
|
|
||||||
send_params.com_if.send(cfdp_packet_in_ccsds.space_packet.pack())
|
|
||||||
# TODO: Log raw CFDP packets similarly to how PUS packets are logged.
|
|
||||||
# - Log full raw format including space packet wrapper
|
|
||||||
# - Log context information: Transaction ID, and PDU type and directive
|
|
||||||
# Could re-use file logger. Should probably do that
|
|
||||||
# print(f"sending packet: [{cfdp_packet_in_ccsds.space_packet.pack()}]")
|
|
||||||
# with open(f"cfdp_packet_{self.cfdp_counter}", "wb") as of:
|
|
||||||
# of.write(cfdp_packet_in_ccsds.space_packet.pack())
|
|
||||||
# self.cfdp_counter += 1
|
|
||||||
elif entry_helper.entry_type == TcQueueEntryType.LOG:
|
|
||||||
log_entry = entry_helper.to_log_entry()
|
|
||||||
_LOGGER.info(log_entry.log_str)
|
|
||||||
self.high_level_file_logger.info(log_entry.log_str)
|
|
||||||
|
|
||||||
def handle_cfdp_procedure(self, info: ProcedureWrapper):
|
|
||||||
cfdp_procedure = info.to_cfdp_procedure()
|
|
||||||
if cfdp_procedure.cfdp_request_type == CfdpRequestType.PUT:
|
|
||||||
if (
|
|
||||||
not self.cfdp_in_ccsds_wrapper.handler.put_request_pending()
|
|
||||||
and not self.cfdp_handler_started
|
|
||||||
):
|
|
||||||
put_req = cfdp_procedure.request_wrapper.to_put_request()
|
|
||||||
put_req.cfg.destination_id = self.cfdp_dest_id
|
|
||||||
_LOGGER.info(
|
|
||||||
f"CFDP: Starting file put request with parameters:\n{put_req}"
|
|
||||||
)
|
|
||||||
self.cfdp_in_ccsds_wrapper.handler.cfdp_handler.put_request(put_req)
|
|
||||||
self.cfdp_handler_started = True
|
|
||||||
|
|
||||||
for source_pair, dest_pair in self.cfdp_in_ccsds_wrapper.handler:
|
|
||||||
pdu, sp = source_pair
|
|
||||||
pdu = cast(PduHolder, pdu)
|
|
||||||
if pdu.is_file_directive:
|
|
||||||
if pdu.pdu_directive_type == DirectiveType.METADATA_PDU:
|
|
||||||
metadata = pdu.to_metadata_pdu()
|
|
||||||
self.queue_helper.add_log_cmd(
|
|
||||||
"CFDP Source: Sending Metadata PDU for file with size "
|
|
||||||
f"{metadata.file_size}"
|
|
||||||
)
|
|
||||||
elif pdu.pdu_directive_type == DirectiveType.EOF_PDU:
|
|
||||||
self.queue_helper.add_log_cmd(
|
|
||||||
"CFDP Source: Sending EOF PDU"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
fd_pdu = pdu.to_file_data_pdu()
|
|
||||||
self.queue_helper.add_log_cmd(
|
|
||||||
"CFDP Source: Sending File Data PDU for segment at offset "
|
|
||||||
f"{fd_pdu.offset} with length {len(fd_pdu.file_data)}"
|
|
||||||
)
|
|
||||||
self.queue_helper.add_ccsds_tc(sp)
|
|
||||||
self.cfdp_in_ccsds_wrapper.handler.confirm_source_packet_sent()
|
|
||||||
self.cfdp_in_ccsds_wrapper.handler.source_handler.state_machine()
|
|
||||||
|
|
||||||
def queue_finished_cb(self, info: ProcedureWrapper):
|
|
||||||
if info is not None:
|
|
||||||
if info.proc_type == TcQueueEntryType.PUS_TC:
|
|
||||||
def_proc = info.to_def_procedure()
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Finished queue for service {def_proc.service} and op code"
|
|
||||||
f" {def_proc.op_code}"
|
|
||||||
)
|
|
||||||
elif info.proc_type == TcProcedureType.CFDP:
|
|
||||||
_LOGGER.info("Finished CFDP queue")
|
|
||||||
|
|
||||||
|
|
||||||
def setup_params() -> (SetupWrapper, int):
|
def setup_params() -> (SetupWrapper, int):
|
||||||
hook_obj = EiveHookObject(default_json_path())
|
hook_obj = EiveHookObject(default_json_path())
|
||||||
params = SetupParams()
|
params = SetupParams()
|
||||||
@ -344,7 +120,10 @@ def setup_params() -> (SetupWrapper, int):
|
|||||||
post_arg_parsing_wrapper.set_params_with_prompts(proc_param_wrapper)
|
post_arg_parsing_wrapper.set_params_with_prompts(proc_param_wrapper)
|
||||||
else:
|
else:
|
||||||
post_arg_parsing_wrapper.set_params_without_prompts(proc_param_wrapper)
|
post_arg_parsing_wrapper.set_params_without_prompts(proc_param_wrapper)
|
||||||
hk_level = int(post_arg_parsing_wrapper.args_raw.hk)
|
if hasattr(post_arg_parsing_wrapper.args_raw, "hk"):
|
||||||
|
hk_level = int(post_arg_parsing_wrapper.args_raw.hk)
|
||||||
|
else:
|
||||||
|
hk_level = 0
|
||||||
params.apid = PUS_APID
|
params.apid = PUS_APID
|
||||||
if params.com_if is None:
|
if params.com_if is None:
|
||||||
raise ValueError("could not determine a COM interface.")
|
raise ValueError("could not determine a COM interface.")
|
||||||
@ -361,6 +140,15 @@ def setup_cfdp_handler() -> CfdpInCcsdsWrapper:
|
|||||||
indication_cfg=IndicationCfg(),
|
indication_cfg=IndicationCfg(),
|
||||||
default_fault_handlers=fh_base,
|
default_fault_handlers=fh_base,
|
||||||
)
|
)
|
||||||
|
self_as_remote = RemoteEntityCfg(
|
||||||
|
closure_requested=False,
|
||||||
|
entity_id=CFDP_LOCAL_ENTITY_ID,
|
||||||
|
max_file_segment_len=990,
|
||||||
|
check_limit=None,
|
||||||
|
crc_on_transmission=False,
|
||||||
|
crc_type=ChecksumType.CRC_32,
|
||||||
|
default_transmission_mode=TransmissionMode.UNACKNOWLEDGED,
|
||||||
|
)
|
||||||
remote_cfg = RemoteEntityCfg(
|
remote_cfg = RemoteEntityCfg(
|
||||||
closure_requested=False,
|
closure_requested=False,
|
||||||
entity_id=CFDP_REMOTE_ENTITY_ID,
|
entity_id=CFDP_REMOTE_ENTITY_ID,
|
||||||
@ -379,7 +167,7 @@ def setup_cfdp_handler() -> CfdpInCcsdsWrapper:
|
|||||||
cfdp_user = EiveCfdpUser()
|
cfdp_user = EiveCfdpUser()
|
||||||
cfdp_in_ccsds_handler = CfdpInCcsdsHandler(
|
cfdp_in_ccsds_handler = CfdpInCcsdsHandler(
|
||||||
cfg=cfdp_cfg,
|
cfg=cfdp_cfg,
|
||||||
remote_cfgs=[remote_cfg],
|
remote_cfgs=[remote_cfg, self_as_remote],
|
||||||
ccsds_apid=CFDP_APID,
|
ccsds_apid=CFDP_APID,
|
||||||
ccsds_seq_cnt_provider=cfdp_ccsds_seq_count_provider,
|
ccsds_seq_cnt_provider=cfdp_ccsds_seq_count_provider,
|
||||||
cfdp_seq_cnt_provider=cfdp_seq_count_provider,
|
cfdp_seq_cnt_provider=cfdp_seq_count_provider,
|
||||||
@ -410,7 +198,7 @@ def setup_tmtc_handlers(
|
|||||||
high_level_file_logger=printer.file_logger,
|
high_level_file_logger=printer.file_logger,
|
||||||
raw_pus_logger=raw_logger,
|
raw_pus_logger=raw_logger,
|
||||||
gui=gui,
|
gui=gui,
|
||||||
cfdp_in_ccsds_wrapper=cfdp_in_ccsds_wrapper,
|
cfdp_in_ccsds_handler=cfdp_in_ccsds_wrapper.handler,
|
||||||
)
|
)
|
||||||
return ccsds_handler, tc_handler
|
return ccsds_handler, tc_handler
|
||||||
|
|
||||||
@ -461,13 +249,16 @@ def main(): # noqa C901: Complexity okay here.
|
|||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
state = tmtc_backend.periodic_op(None)
|
state = tmtc_backend.periodic_op(None)
|
||||||
tc_handler.cfdp_in_ccsds_wrapper.handler.fsm()
|
tc_handler.cfdp_in_ccsds_handler.state_machine()
|
||||||
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif state.request == BackendRequest.DELAY_IDLE:
|
elif state.request == BackendRequest.DELAY_IDLE:
|
||||||
_LOGGER.info("TMTC Client in IDLE mode")
|
_LOGGER.info("TMTC Client in IDLE mode")
|
||||||
time.sleep(3.0)
|
time.sleep(3.0)
|
||||||
elif state.request == BackendRequest.DELAY_LISTENER:
|
elif state.request == BackendRequest.DELAY_LISTENER:
|
||||||
|
if tc_handler.proxy_op:
|
||||||
|
time.sleep(0.1)
|
||||||
|
continue
|
||||||
if tc_handler.cfdp_done():
|
if tc_handler.cfdp_done():
|
||||||
_LOGGER.info("CFDP transaction done, closing client")
|
_LOGGER.info("CFDP transaction done, closing client")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
Reference in New Issue
Block a user