309 lines
10 KiB
Python
Executable File
309 lines
10 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import logging
|
|
import sys
|
|
import time
|
|
import traceback
|
|
from pathlib import Path
|
|
|
|
from eive_tmtc.cfdp.fault_handler import EiveCfdpFaultHandler
|
|
from eive_tmtc.cfdp.user import EiveCfdpUser
|
|
from spacepackets.ccsds import SPACE_PACKET_HEADER_SIZE
|
|
from spacepackets.cfdp import (
|
|
ChecksumType,
|
|
TransmissionMode,
|
|
DirectiveType,
|
|
PduFactory,
|
|
PduType,
|
|
)
|
|
|
|
from eive_tmtc.pus_tc.tc_handler import TcHandler
|
|
from tmtccmd.logging import add_colorlog_console_logger
|
|
from tmtccmd.cfdp.handler import CfdpInCcsdsHandler
|
|
from tmtccmd.cfdp.mib import (
|
|
LocalEntityCfg,
|
|
IndicationCfg,
|
|
RemoteEntityCfg,
|
|
)
|
|
|
|
try:
|
|
import spacepackets
|
|
except ImportError as error:
|
|
print(error)
|
|
print("Python spacepackets module could not be imported")
|
|
print(
|
|
'Install with "cd spacepackets && python3 -m pip intall -e ." for interative installation'
|
|
)
|
|
sys.exit(1)
|
|
|
|
try:
|
|
import tmtccmd
|
|
except ImportError:
|
|
run_tmtc_commander = None
|
|
initialize_tmtc_commander = None
|
|
tb = traceback.format_exc()
|
|
print(tb)
|
|
print("Python tmtccmd submodule could not be imported")
|
|
sys.exit(1)
|
|
|
|
from spacepackets.ecss import PusVerificator
|
|
from tmtccmd import BackendBase
|
|
from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider
|
|
from tmtccmd.fsfw.tmtc_printer import FsfwTmTcPrinter
|
|
|
|
from tmtccmd.logging.pus import (
|
|
RawTmtcTimedLogWrapper,
|
|
RegularTmtcLogWrapper,
|
|
TimedLogWhen,
|
|
)
|
|
from tmtccmd.pus import VerificationWrapper
|
|
from tmtccmd.tm import SpecificApidHandlerBase, GenericApidHandlerBase, CcsdsTmHandler
|
|
from tmtccmd.core import BackendRequest
|
|
from tmtccmd.config import (
|
|
default_json_path,
|
|
SetupWrapper,
|
|
params_to_procedure_conversion,
|
|
)
|
|
from tmtccmd.config.args import (
|
|
SetupParams,
|
|
PreArgsParsingWrapper,
|
|
ProcedureParamsWrapper,
|
|
)
|
|
from eive_tmtc import APP_LOGGER
|
|
from eive_tmtc import __version__
|
|
from eive_tmtc.config.definitions import (
|
|
PUS_APID,
|
|
CFDP_APID,
|
|
CFDP_LOCAL_ENTITY_ID,
|
|
CFDP_REMOTE_ENTITY_ID,
|
|
)
|
|
from eive_tmtc.config.hook import EiveHookObject
|
|
from eive_tmtc.pus_tm.pus_demux import pus_factory_hook
|
|
|
|
_LOGGER = APP_LOGGER
|
|
_LOG_LEVEL = logging.INFO
|
|
|
|
# Put rotating file logger parameters here for quick changes
|
|
ROTATING_TIMED_LOGGER_INTERVAL_WHEN = TimedLogWhen.PER_MINUTE
|
|
ROTATING_TIMED_LOGGER_INTERVAL = 30
|
|
|
|
|
|
class PusHandler(SpecificApidHandlerBase):
|
|
def __init__(
|
|
self,
|
|
wrapper: VerificationWrapper,
|
|
printer: FsfwTmTcPrinter,
|
|
raw_logger: RawTmtcTimedLogWrapper,
|
|
hk_level: int,
|
|
):
|
|
super().__init__(PUS_APID, None)
|
|
self.printer = printer
|
|
self.verif_wrapper = wrapper
|
|
self.raw_logger = raw_logger
|
|
self.hk_level = hk_level
|
|
|
|
def handle_tm(self, packet: bytes, _user_args: any):
|
|
# with open("tc.bin", "wb") as of:
|
|
# of.write(packet)
|
|
pus_factory_hook(
|
|
packet, self.verif_wrapper, self.printer, self.raw_logger, self.hk_level
|
|
)
|
|
|
|
|
|
class UnknownApidHandler(GenericApidHandlerBase):
|
|
def handle_tm(self, apid: int, packet: bytes, _user_args: any):
|
|
_LOGGER.warning(
|
|
f"Packet with unknown APID {apid} detected: {packet.hex(sep=',')}"
|
|
)
|
|
|
|
|
|
class CustomCcsdsTmHandler(CcsdsTmHandler):
|
|
def user_hook(self, apid: int, packet: bytes):
|
|
_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(
|
|
f"Received File Directive PDU with type {pdu_base.directive_type!r} TM"
|
|
)
|
|
self.handler.pass_pdu_packet(pdu_base)
|
|
|
|
|
|
def setup_params() -> (SetupWrapper, int):
|
|
hook_obj = EiveHookObject(default_json_path())
|
|
params = SetupParams()
|
|
parser_wrapper = PreArgsParsingWrapper()
|
|
parser_wrapper.create_default_parent_parser()
|
|
parser_wrapper.create_default_parser()
|
|
tmtc_parser, cfdp_parser = parser_wrapper.add_def_proc_and_cfdp_as_subparsers()
|
|
tmtc_parser.add_argument(
|
|
"--hk",
|
|
help="HK output level",
|
|
default=2,
|
|
)
|
|
post_arg_parsing_wrapper = parser_wrapper.parse(
|
|
setup_params=params, hook_obj=hook_obj
|
|
)
|
|
tmtccmd.init_printout(post_arg_parsing_wrapper.use_gui)
|
|
use_prompts = not post_arg_parsing_wrapper.use_gui
|
|
proc_param_wrapper = ProcedureParamsWrapper()
|
|
if use_prompts:
|
|
post_arg_parsing_wrapper.set_params_with_prompts(proc_param_wrapper)
|
|
else:
|
|
post_arg_parsing_wrapper.set_params_without_prompts(proc_param_wrapper)
|
|
hk_level = int(post_arg_parsing_wrapper.args_raw.hk)
|
|
params.apid = PUS_APID
|
|
if params.com_if is None:
|
|
raise ValueError("could not determine a COM interface.")
|
|
setup_wrapper = SetupWrapper(
|
|
hook_obj=hook_obj, setup_params=params, proc_param_wrapper=proc_param_wrapper
|
|
)
|
|
return setup_wrapper, hk_level
|
|
|
|
|
|
def setup_cfdp_handler() -> CfdpInCcsdsWrapper:
|
|
fh_base = EiveCfdpFaultHandler()
|
|
cfdp_cfg = LocalEntityCfg(
|
|
local_entity_id=CFDP_LOCAL_ENTITY_ID,
|
|
indication_cfg=IndicationCfg(),
|
|
default_fault_handlers=fh_base,
|
|
)
|
|
remote_cfg = RemoteEntityCfg(
|
|
closure_requested=False,
|
|
entity_id=CFDP_REMOTE_ENTITY_ID,
|
|
max_file_segment_len=990,
|
|
check_limit=None,
|
|
crc_on_transmission=False,
|
|
crc_type=ChecksumType.CRC_32,
|
|
default_transmission_mode=TransmissionMode.UNACKNOWLEDGED,
|
|
)
|
|
cfdp_seq_count_provider = FileSeqCountProvider(
|
|
max_bit_width=16, file_name=Path("seqcnt_cfdp_transaction.txt")
|
|
)
|
|
cfdp_ccsds_seq_count_provider = PusFileSeqCountProvider(
|
|
file_name=Path("seqcnt_cfdp_in_ccsds.txt")
|
|
)
|
|
cfdp_user = EiveCfdpUser()
|
|
cfdp_in_ccsds_handler = CfdpInCcsdsHandler(
|
|
cfg=cfdp_cfg,
|
|
remote_cfgs=[remote_cfg],
|
|
ccsds_apid=CFDP_APID,
|
|
ccsds_seq_cnt_provider=cfdp_ccsds_seq_count_provider,
|
|
cfdp_seq_cnt_provider=cfdp_seq_count_provider,
|
|
user=cfdp_user,
|
|
)
|
|
return CfdpInCcsdsWrapper(cfdp_in_ccsds_handler)
|
|
|
|
|
|
def setup_tmtc_handlers(
|
|
verificator: PusVerificator,
|
|
printer: FsfwTmTcPrinter,
|
|
raw_logger: RawTmtcTimedLogWrapper,
|
|
gui: bool,
|
|
hk_level: int,
|
|
) -> (CcsdsTmHandler, TcHandler):
|
|
cfdp_in_ccsds_wrapper = setup_cfdp_handler()
|
|
verification_wrapper = VerificationWrapper(
|
|
verificator, _LOGGER, printer.file_logger
|
|
)
|
|
pus_handler = PusHandler(verification_wrapper, printer, raw_logger, hk_level)
|
|
ccsds_handler = CustomCcsdsTmHandler(generic_handler=UnknownApidHandler(None))
|
|
ccsds_handler.add_apid_handler(pus_handler)
|
|
ccsds_handler.add_apid_handler(cfdp_in_ccsds_wrapper)
|
|
seq_count_provider = PusFileSeqCountProvider()
|
|
tc_handler = TcHandler(
|
|
seq_count_provider=seq_count_provider,
|
|
pus_verificator=verificator,
|
|
high_level_file_logger=printer.file_logger,
|
|
raw_pus_logger=raw_logger,
|
|
gui=gui,
|
|
cfdp_in_ccsds_wrapper=cfdp_in_ccsds_wrapper,
|
|
)
|
|
return ccsds_handler, tc_handler
|
|
|
|
|
|
def setup_backend(
|
|
setup_wrapper: SetupWrapper,
|
|
tc_handler: TcHandler,
|
|
ccsds_handler: CcsdsTmHandler,
|
|
) -> BackendBase:
|
|
init_proc = params_to_procedure_conversion(setup_wrapper.proc_param_wrapper)
|
|
tmtc_backend = tmtccmd.create_default_tmtc_backend(
|
|
setup_wrapper=setup_wrapper,
|
|
tm_handler=ccsds_handler,
|
|
tc_handler=tc_handler,
|
|
init_procedure=init_proc,
|
|
)
|
|
tmtccmd.start(tmtc_backend=tmtc_backend, hook_obj=setup_wrapper.hook_obj)
|
|
return tmtc_backend
|
|
|
|
|
|
def main(): # noqa C901: Complexity okay here.
|
|
print(f"-- eive tmtc v{__version__} --")
|
|
print(f"-- spacepackets v{spacepackets.__version__} --")
|
|
add_colorlog_console_logger(_LOGGER)
|
|
# TODO: -V CLI argument to enable this?
|
|
_LOGGER.setLevel(_LOG_LEVEL)
|
|
try:
|
|
setup_wrapper, hk_level = setup_params()
|
|
except KeyboardInterrupt as e:
|
|
_LOGGER.info(f"{e}. Exiting")
|
|
sys.exit(0)
|
|
|
|
tmtc_logger = RegularTmtcLogWrapper()
|
|
printer = FsfwTmTcPrinter(tmtc_logger.logger)
|
|
raw_logger = RawTmtcTimedLogWrapper(
|
|
when=ROTATING_TIMED_LOGGER_INTERVAL_WHEN,
|
|
interval=ROTATING_TIMED_LOGGER_INTERVAL,
|
|
)
|
|
pus_verificator = PusVerificator()
|
|
ccsds_handler, tc_handler = setup_tmtc_handlers(
|
|
pus_verificator, printer, raw_logger, setup_wrapper.params.use_gui, hk_level
|
|
)
|
|
|
|
tmtccmd.setup(setup_wrapper)
|
|
tmtc_backend = setup_backend(
|
|
setup_wrapper=setup_wrapper, ccsds_handler=ccsds_handler, tc_handler=tc_handler
|
|
)
|
|
try:
|
|
while True:
|
|
state = tmtc_backend.periodic_op(None)
|
|
tc_handler.cfdp_in_ccsds_wrapper.handler.fsm()
|
|
if state.request == BackendRequest.TERMINATION_NO_ERROR:
|
|
sys.exit(0)
|
|
elif state.request == BackendRequest.DELAY_IDLE:
|
|
_LOGGER.info("TMTC Client in IDLE mode")
|
|
time.sleep(3.0)
|
|
elif state.request == BackendRequest.DELAY_LISTENER:
|
|
if tc_handler.cfdp_done():
|
|
_LOGGER.info("CFDP transaction done, closing client")
|
|
sys.exit(0)
|
|
time.sleep(0.5)
|
|
elif state.request == BackendRequest.DELAY_CUSTOM:
|
|
if state.next_delay.total_seconds() < 0.5:
|
|
time.sleep(state.next_delay.total_seconds())
|
|
else:
|
|
time.sleep(0.5)
|
|
elif state.request == BackendRequest.CALL_NEXT:
|
|
pass
|
|
except KeyboardInterrupt:
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|